continuum-toolkit 1.0.0__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.
Files changed (72) hide show
  1. cli/__init__.py +12 -0
  2. cli/main.py +415 -0
  3. confidence/__init__.py +13 -0
  4. confidence/calculator.py +308 -0
  5. confidence/models.py +40 -0
  6. context/__init__.py +17 -0
  7. context/models.py +108 -0
  8. context/pruner.py +228 -0
  9. context/selector.py +193 -0
  10. continuum_toolkit-1.0.0.dist-info/METADATA +511 -0
  11. continuum_toolkit-1.0.0.dist-info/RECORD +72 -0
  12. continuum_toolkit-1.0.0.dist-info/WHEEL +5 -0
  13. continuum_toolkit-1.0.0.dist-info/entry_points.txt +2 -0
  14. continuum_toolkit-1.0.0.dist-info/licenses/LICENSE +21 -0
  15. continuum_toolkit-1.0.0.dist-info/top_level.txt +13 -0
  16. contradictions/__init__.py +19 -0
  17. contradictions/detector.py +442 -0
  18. contradictions/models.py +75 -0
  19. core/__init__.py +97 -0
  20. core/enums.py +130 -0
  21. core/evidence.py +117 -0
  22. core/interfaces.py +209 -0
  23. core/schema.py +391 -0
  24. core/serializer.py +84 -0
  25. core/state_models.py +530 -0
  26. daemon/__init__.py +12 -0
  27. daemon/service.py +170 -0
  28. extractors/__init__.py +51 -0
  29. extractors/base.py +117 -0
  30. extractors/config/parsers.py +288 -0
  31. extractors/config/secret_sanitizer.py +91 -0
  32. extractors/config_extractor.py +172 -0
  33. extractors/conversation/analyzers.py +193 -0
  34. extractors/conversation/models.py +148 -0
  35. extractors/conversation_extractor.py +166 -0
  36. extractors/git_extractor.py +305 -0
  37. extractors/parsers/base.py +91 -0
  38. extractors/parsers/comment_parser.py +51 -0
  39. extractors/parsers/js_ts_parser.py +171 -0
  40. extractors/parsers/python_parser.py +180 -0
  41. extractors/verification/runners.py +278 -0
  42. extractors/verification_extractor.py +263 -0
  43. extractors/workspace_extractor.py +221 -0
  44. graph/__init__.py +24 -0
  45. graph/diff.py +109 -0
  46. graph/manager.py +473 -0
  47. graph/models.py +62 -0
  48. graph/propagator.py +194 -0
  49. graph/query.py +86 -0
  50. graph/snapshot.py +85 -0
  51. handoff/__init__.py +28 -0
  52. handoff/adapters/__init__.py +45 -0
  53. handoff/adapters/base.py +90 -0
  54. handoff/adapters/claude_adapter.py +176 -0
  55. handoff/adapters/codex_gpt_adapter.py +151 -0
  56. handoff/adapters/gemini_adapter.py +151 -0
  57. handoff/adapters/local_model_adapter.py +130 -0
  58. handoff/models.py +88 -0
  59. handoff/packager.py +288 -0
  60. pipeline/__init__.py +9 -0
  61. pipeline/orchestrator.py +260 -0
  62. resolution/__init__.py +15 -0
  63. resolution/resolver.py +311 -0
  64. storage/__init__.py +18 -0
  65. storage/hooks.py +125 -0
  66. storage/manager.py +127 -0
  67. storage/models.py +39 -0
  68. storage/recovery.py +98 -0
  69. watcher/__init__.py +17 -0
  70. watcher/detector.py +136 -0
  71. watcher/models.py +62 -0
  72. watcher/updater.py +163 -0
cli/__init__.py ADDED
@@ -0,0 +1,12 @@
1
+ """
2
+ Project Continuum - CLI Package Exports
3
+ =======================================
4
+ Milestone 7 - Phase 18: Continuum Daemon & CLI.
5
+ """
6
+
7
+ from cli.main import main, build_parser
8
+
9
+ __all__ = [
10
+ "main",
11
+ "build_parser",
12
+ ]
cli/main.py ADDED
@@ -0,0 +1,415 @@
1
+ """
2
+ Project Continuum - Command Line Interface (CLI)
3
+ =================================================
4
+ Milestone 7 - Phase 18: Continuum Daemon & CLI.
5
+ Provides standard developer commands:
6
+ - continuum init
7
+ - continuum status
8
+ - continuum scan
9
+ - continuum verify
10
+ - continuum graph
11
+ - continuum handoff
12
+ - continuum daemon
13
+ """
14
+
15
+ import argparse
16
+ import json
17
+ import os
18
+ from pathlib import Path
19
+ import sys
20
+ from typing import List, Optional
21
+
22
+ # Ensure project root is on sys.path when executed directly as a script
23
+ _PROJECT_ROOT = Path(__file__).resolve().parent.parent
24
+ if str(_PROJECT_ROOT) not in sys.path:
25
+ sys.path.insert(0, str(_PROJECT_ROOT))
26
+
27
+ from core.enums import EvidenceType, Status, TargetModel
28
+ from core.state_models import CanonicalProjectState, ProjectState
29
+ from daemon.service import ContinuumDaemon
30
+ from extractors.workspace_extractor import WorkspaceEvidenceExtractor
31
+ from extractors.config_extractor import ConfigEvidenceExtractor
32
+ from extractors.git_extractor import GitEvidenceExtractor
33
+ from graph.manager import StateGraphManager
34
+ from handoff.packager import UniversalHandoffPackager
35
+ from handoff.adapters import get_adapter_for_model
36
+ from storage.manager import ContinuumStorageManager
37
+ from storage.hooks import GitHookManager
38
+ from storage.recovery import StateRecoveryManager
39
+
40
+
41
+ def build_parser() -> argparse.ArgumentParser:
42
+ """Builds the Continuum CLI command argument parser."""
43
+ parser = argparse.ArgumentParser(
44
+ prog="continuum",
45
+ description="Project Continuum — AI Work Continuity & Agent Handoff System"
46
+ )
47
+ parser.add_argument("--version", action="version", version="continuum v1.0.0 (Python 3.10+ | Schema 1.0.0)")
48
+ subparsers = parser.add_subparsers(dest="command", help="Available subcommands")
49
+
50
+ # 1. init
51
+ init_parser = subparsers.add_parser("init", help="Initialize .continuum storage and Git hooks in workspace")
52
+ init_parser.add_argument("--path", default=".", help="Workspace path (default: current directory)")
53
+
54
+ # 2. status
55
+ status_parser = subparsers.add_parser("status", help="Print verified project state, contradictions, and next actions")
56
+ status_parser.add_argument("--path", default=".", help="Workspace path")
57
+ status_parser.add_argument("--json", action="store_true", help="Output status in JSON format")
58
+
59
+ # 3. scan
60
+ scan_parser = subparsers.add_parser("scan", help="Perform full workspace evidence extraction and update state")
61
+ scan_parser.add_argument("--path", default=".", help="Workspace path")
62
+
63
+ # 4. graph
64
+ graph_parser = subparsers.add_parser("graph", help="Inspect Canonical State Graph (DAG)")
65
+ graph_parser.add_argument("--path", default=".", help="Workspace path")
66
+ graph_parser.add_argument("--mermaid", action="store_true", help="Export Mermaid diagram")
67
+ graph_parser.add_argument("--stats", action="store_true", help="Show topological metrics")
68
+
69
+ # 5. handoff
70
+ handoff_parser = subparsers.add_parser("handoff", help="Generate AI handoff package")
71
+ handoff_parser.add_argument("--path", default=".", help="Workspace path")
72
+ handoff_parser.add_argument("--model", choices=["auto", "universal", "claude", "codex", "gpt", "gemini", "local"], default="auto", help="Target AI model format (default: auto omni-model)")
73
+ handoff_parser.add_argument("--output-dir", default=None, help="Directory to save the handoff package (default: ./ai_handoff)")
74
+
75
+ # 6. daemon
76
+ daemon_parser = subparsers.add_parser("daemon", help="Manage background workspace observer daemon")
77
+ daemon_parser.add_argument("action", choices=["start", "stop", "status", "run-once"], help="Daemon action to perform")
78
+ daemon_parser.add_argument("--path", default=".", help="Workspace path")
79
+
80
+ return parser
81
+
82
+
83
+ def handle_init(args: argparse.Namespace) -> int:
84
+ ws = Path(args.path).resolve()
85
+ storage = ContinuumStorageManager(str(ws))
86
+ storage.initialize_storage()
87
+
88
+ hooks = GitHookManager(str(ws))
89
+ if hooks.is_git_repo():
90
+ hook_res = hooks.install_hooks()
91
+ print(f"[OK] Initialized Continuum storage at {storage.continuum_dir}")
92
+ print(f"[OK] Installed Git hooks: post-commit={hook_res.get('post-commit')}, pre-commit={hook_res.get('pre-commit')}")
93
+ else:
94
+ print(f"[OK] Initialized Continuum storage at {storage.continuum_dir} (Non-git workspace)")
95
+
96
+ return 0
97
+
98
+
99
+ def handle_status(args: argparse.Namespace) -> int:
100
+ ws = Path(args.path).resolve()
101
+ storage = ContinuumStorageManager(str(ws))
102
+
103
+ if not storage.state_exists():
104
+ print(f"[WARN] No active Continuum state found at {ws}. Run 'continuum scan' or 'continuum init' first.")
105
+ return 1
106
+
107
+ state = storage.load_state(validate=True)
108
+
109
+ if getattr(args, "json", False):
110
+ print(json.dumps(state.to_dict(), indent=2))
111
+ return 0
112
+
113
+ p_state = state.project_state
114
+ c_state = state.conversational_state
115
+ exec_state = state.agent_execution_state
116
+
117
+ print("=" * 70)
118
+ print(f"PROJECT CONTINUUM — VERIFIED PROJECT STATUS")
119
+ print("=" * 70)
120
+ print(f"Project ID: {state.project_id} (Schema: {state.schema_version})")
121
+ print(f"Workspace Root: {p_state.root_path}")
122
+ print(f"Source Files: {len(p_state.files)} file(s)")
123
+ print(f"AST Symbols: {len(p_state.symbols)} symbol(s)")
124
+ print(f"Build Status: [{p_state.build_status.value}]")
125
+ print(f"Graph Nodes: {len(state.graph_nodes)} nodes, {len(state.graph_edges)} edges")
126
+ print(f"Contradictions: {len(state.contradictions)} discrepancy record(s)")
127
+
128
+ if exec_state.next_action:
129
+ na = exec_state.next_action
130
+ print(f"\n[NEXT ACTION] [{na.action_type}] {na.target_uri}")
131
+ print(f" Description: {na.description}")
132
+ print("=" * 70)
133
+ return 0
134
+
135
+
136
+ def handle_scan(args: argparse.Namespace) -> int:
137
+ ws = Path(args.path).resolve()
138
+ storage = ContinuumStorageManager(str(ws))
139
+ storage.initialize_storage()
140
+
141
+ print(f"[SCAN] Scanning workspace at {ws}...")
142
+ ws_extractor = WorkspaceEvidenceExtractor()
143
+ ev_list = ws_extractor.extract(str(ws))
144
+
145
+ # Construct canonical state from extracted evidence
146
+ p_state = ProjectState(root_path=str(ws))
147
+ for ev in ev_list:
148
+ if ev.type == EvidenceType.SOURCE_CODE or ev.type.value == "SOURCE_CODE":
149
+ files = ev.raw_payload.get("file_list", ev.raw_payload.get("files", []))
150
+ p_state.files.extend(files)
151
+ langs = list(ev.raw_payload.get("languages", {}).keys()) or ev.raw_payload.get("detected_languages", [])
152
+ p_state.detected_languages.extend(langs)
153
+ elif ev.type == EvidenceType.AST_SYMBOL or ev.type.value == "AST_SYMBOL":
154
+ from core.state_models import AstSymbol
155
+ sym_dicts = ev.raw_payload.get("symbols", [])
156
+ for sd in sym_dicts:
157
+ p_state.symbols.append(AstSymbol.from_dict(sd))
158
+
159
+ state = CanonicalProjectState(
160
+ project_id=f"proj_{ws.name}",
161
+ project_state=p_state
162
+ )
163
+
164
+ # Build DAG
165
+ graph_manager = StateGraphManager(state)
166
+ graph_manager.build_from_canonical_state(state)
167
+
168
+ storage.save_state(state, create_history_snapshot=True)
169
+ print(f"[OK] Scan complete. Harvested {len(p_state.files)} files, {len(p_state.symbols)} symbols.")
170
+ return 0
171
+
172
+
173
+ def handle_graph(args: argparse.Namespace) -> int:
174
+ ws = Path(args.path).resolve()
175
+ storage = ContinuumStorageManager(str(ws))
176
+ if not storage.state_exists():
177
+ print(f"[WARN] No active state. Run 'continuum scan' first.")
178
+ return 1
179
+
180
+ state = storage.load_state(validate=True)
181
+ graph_manager = StateGraphManager(state)
182
+
183
+ if getattr(args, "mermaid", False):
184
+ print(graph_manager.export_mermaid())
185
+ return 0
186
+
187
+ if getattr(args, "stats", False):
188
+ stats = graph_manager.get_stats()
189
+ print(json.dumps(stats.to_dict(), indent=2))
190
+ return 0
191
+
192
+ # Default: summary
193
+ stats = graph_manager.get_stats()
194
+ print(f"Canonical State Graph: {stats.total_nodes} nodes, {stats.total_edges} edges.")
195
+ for k, v in stats.nodes_by_type.items():
196
+ print(f" - {k}: {v}")
197
+ return 0
198
+
199
+
200
+ def handle_handoff(args: argparse.Namespace) -> int:
201
+ ws = Path(args.path).resolve()
202
+ storage = ContinuumStorageManager(str(ws))
203
+
204
+ # Auto-initialize and scan if not previously initialized (1-command magic)
205
+ if not storage.state_exists():
206
+ print(f"[AUTO] Initializing and scanning workspace at {ws}...")
207
+ storage.initialize_storage()
208
+ ws_extractor = WorkspaceEvidenceExtractor()
209
+ config_extractor = ConfigEvidenceExtractor()
210
+ git_extractor = GitEvidenceExtractor()
211
+
212
+ state = CanonicalProjectState(schema_version="1.0.0", project_id=f"proj_{ws.name}")
213
+ ws_extractor.populate_project_state(str(ws), state)
214
+ config_extractor.populate_project_state(str(ws), state)
215
+ git_extractor.populate_project_state(str(ws), state)
216
+ storage.save_state(state)
217
+ print(f"[OK] Workspace parsed: {len(state.project_state.files)} files, {len(state.project_state.symbols)} symbols.")
218
+ else:
219
+ state = storage.load_state(validate=True)
220
+
221
+ model_choice = getattr(args, "model", "auto").lower()
222
+
223
+ out_dir = args.output_dir or str(ws / "ai_handoff")
224
+ out_path = Path(out_dir).resolve()
225
+ out_path.mkdir(parents=True, exist_ok=True)
226
+
227
+ if model_choice in ("auto", "all", "universal"):
228
+ # Auto mode: Generate Universal package + all model-specific profiles
229
+ packager = UniversalHandoffPackager()
230
+ pkg = packager.generate_package(state)
231
+ paths = pkg.save_to_directory(str(out_path))
232
+
233
+ # Also generate dedicated model views (claude_handoff.md, gpt_handoff.md, gemini_handoff.md)
234
+ claude_adapter = get_adapter_for_model(TargetModel.CLAUDE)
235
+ gpt_adapter = get_adapter_for_model(TargetModel.CODEX_GPT)
236
+ gemini_adapter = get_adapter_for_model(TargetModel.GEMINI)
237
+
238
+ (out_path / "claude_handoff.md").write_text(claude_adapter.generate_handoff(state)["handoff.md"], encoding="utf-8")
239
+ (out_path / "gpt_handoff.md").write_text(gpt_adapter.generate_handoff(state)["handoff.md"], encoding="utf-8")
240
+ (out_path / "gemini_handoff.md").write_text(gemini_adapter.generate_handoff(state)["handoff.md"], encoding="utf-8")
241
+
242
+ output_text = f"""
243
+ ◈ [HANDOFF READY] Generated Omni-Model Continuity Package:
244
+
245
+ Location: {out_path}
246
+
247
+ ✦ Universal Handoff → {out_path / 'handoff.md'} (Works for ANY model)
248
+
249
+ ✦ Claude Optimized → {out_path / 'claude_handoff.md'}
250
+
251
+ ✦ GPT / Codex Plan → {out_path / 'gpt_handoff.md'}
252
+
253
+ ✦ Gemini Hierarchy → {out_path / 'gemini_handoff.md'}
254
+
255
+ ✦ Machine State → {out_path / 'project-state.json'}
256
+
257
+ ➤ [NEXT ACTION] Paste this prompt into your next AI Agent chat:
258
+
259
+ "Read {out_path.name}/handoff.md and continue the project."
260
+
261
+ """
262
+ try:
263
+ sys.stdout.buffer.write(output_text.encode("utf-8"))
264
+ sys.stdout.buffer.flush()
265
+ except Exception:
266
+ print(output_text)
267
+ return 0
268
+
269
+ model_map = {
270
+ "claude": TargetModel.CLAUDE,
271
+ "codex": TargetModel.CODEX_GPT,
272
+ "gpt": TargetModel.CODEX_GPT,
273
+ "gemini": TargetModel.GEMINI,
274
+ "local": TargetModel.LOCAL_LLM,
275
+ }
276
+ target_enum = model_map.get(model_choice, TargetModel.CODEX_GPT)
277
+ adapter = get_adapter_for_model(target_enum)
278
+ handoff_dict = adapter.generate_handoff(state)
279
+
280
+ for fname, content in handoff_dict.items():
281
+ (out_path / fname).write_text(content, encoding="utf-8")
282
+
283
+ output_text = f"""
284
+ ◈ [HANDOFF READY] Generated {target_enum.value} Continuity Package:
285
+
286
+ Location: {out_path}
287
+
288
+ ➤ [NEXT ACTION] Paste this prompt into your next AI Agent chat:
289
+
290
+ "Read {out_path.name}/handoff.md and continue the project."
291
+
292
+ """
293
+ try:
294
+ sys.stdout.buffer.write(output_text.encode("utf-8"))
295
+ sys.stdout.buffer.flush()
296
+ except Exception:
297
+ print(output_text)
298
+
299
+ return 0
300
+
301
+
302
+ def handle_daemon(args: argparse.Namespace) -> int:
303
+ ws = Path(args.path).resolve()
304
+ daemon = ContinuumDaemon(str(ws))
305
+
306
+ if args.action == "start":
307
+ status = daemon.start(run_in_background=True)
308
+ print(f"[OK] Continuum daemon started in background (PID: {status.pid})")
309
+ return 0
310
+ elif args.action == "stop":
311
+ status = daemon.stop()
312
+ print(f"[OK] Continuum daemon stopped.")
313
+ return 0
314
+ elif args.action == "status":
315
+ status = daemon.get_status()
316
+ print(f"Continuum Daemon Status: {'RUNNING' if status.is_running else 'STOPPED'}")
317
+ if status.pid:
318
+ print(f" PID: {status.pid}")
319
+ print(f" Cycles Completed: {status.cycles_completed}")
320
+ print(f" Changes Processed: {status.total_changes_processed}")
321
+ return 0
322
+ elif args.action == "run-once":
323
+ res = daemon.run_once()
324
+ print(f"[OK] Daemon single pass complete: {res}")
325
+ return 0
326
+
327
+ return 1
328
+
329
+
330
+ def print_welcome_hub() -> None:
331
+ """Prints a solid, unbroken block-font terminal welcome hub with Natural Blood Moon palette."""
332
+ version = "v1.0.0"
333
+ py_version = f"{sys.version_info.major}.{sys.version_info.minor}.{sys.version_info.micro}"
334
+
335
+ if os.name == "nt":
336
+ os.system("")
337
+
338
+ RESET = "\033[0m"
339
+ BOLD = "\033[1m"
340
+ GRAY = "\033[90m"
341
+
342
+ def rgb(r: int, g: int, b: int) -> str:
343
+ return f"\033[38;2;{r};{g};{b}m"
344
+
345
+ top_letters = ["█▀▀", "█▀█", "█▄ █", "▀█▀", "█", "█▄ █", "█ █", "█ █", "█▀▄▀█"]
346
+ bot_letters = ["█▄▄", "█▄█", "█ ▀█", " █ ", "█", "█ ▀█", "█▄█", "█▄█", "█ ▀ █"]
347
+
348
+ # Natural Blood Moon Palette (Deep earthy red -> warm rust -> muted blood orange -> deep earthy red)
349
+ natural_blood_moon = [
350
+ rgb(75, 30, 25),
351
+ rgb(105, 40, 30),
352
+ rgb(140, 50, 35),
353
+ rgb(175, 60, 40),
354
+ rgb(200, 75, 45),
355
+ rgb(175, 60, 40),
356
+ rgb(140, 50, 35),
357
+ rgb(105, 40, 30),
358
+ rgb(75, 30, 25),
359
+ ]
360
+
361
+ top_row = " " + " ".join(c + l + RESET for c, l in zip(natural_blood_moon, top_letters))
362
+ bot_row = " " + " ".join(c + l + RESET for c, l in zip(natural_blood_moon, bot_letters))
363
+
364
+ banner_text = f"""
365
+ {top_row}
366
+ {bot_row}
367
+
368
+ AI Work Continuity & Cross-Model Agent Handoff System
369
+ {GRAY}─────────────────────────────────────────────────────────────────────────────{RESET}
370
+
371
+ The one-Command Workflow: $ continuum handoff
372
+
373
+ Automatically captures your project state, extracts AST symbols,
374
+ and generates a ready-to-use 'ai_handoff/' package for your next
375
+ AI Agent (Claude, GPT, Gemini, Cursor, Antigravity, etc.)
376
+
377
+ {GRAY}─────────────────────────────────────────────────────────────────────────────{RESET}
378
+ {GRAY}{version} python {py_version} mit license{RESET}
379
+
380
+ """
381
+ try:
382
+ sys.stdout.buffer.write(banner_text.encode("utf-8"))
383
+ sys.stdout.buffer.flush()
384
+ except Exception:
385
+ print(banner_text)
386
+
387
+
388
+ def main(argv: Optional[List[str]] = None) -> int:
389
+ """Main CLI entrypoint."""
390
+ parser = build_parser()
391
+ args = parser.parse_args(argv)
392
+
393
+ if not args.command:
394
+ print_welcome_hub()
395
+ return 0
396
+
397
+ handlers = {
398
+ "init": handle_init,
399
+ "status": handle_status,
400
+ "scan": handle_scan,
401
+ "graph": handle_graph,
402
+ "handoff": handle_handoff,
403
+ "daemon": handle_daemon,
404
+ }
405
+
406
+ handler = handlers.get(args.command)
407
+ if handler:
408
+ return handler(args)
409
+
410
+ print_welcome_hub()
411
+ return 1
412
+
413
+
414
+ if __name__ == "__main__":
415
+ sys.exit(main())
confidence/__init__.py ADDED
@@ -0,0 +1,13 @@
1
+ """
2
+ Project Continuum - Confidence Subsystem Package
3
+ ================================================
4
+ Milestone 3 - Phase 8.
5
+ """
6
+
7
+ from confidence.models import ConfidenceBreakdown
8
+ from confidence.calculator import ConfidenceCalculator
9
+
10
+ __all__ = [
11
+ "ConfidenceBreakdown",
12
+ "ConfidenceCalculator",
13
+ ]