rockycode 0.1.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 (83) hide show
  1. rockycode/__init__.py +1 -0
  2. rockycode/banner.py +37 -0
  3. rockycode/cli.py +1386 -0
  4. rockycode/config.py +178 -0
  5. rockycode/dream/__init__.py +9 -0
  6. rockycode/dream/core.py +523 -0
  7. rockycode/dream/judge.py +134 -0
  8. rockycode/dream/mining.py +152 -0
  9. rockycode/dream/proposals.py +440 -0
  10. rockycode/engine/__init__.py +10 -0
  11. rockycode/engine/artifact.py +367 -0
  12. rockycode/engine/budget.py +90 -0
  13. rockycode/engine/checks.py +157 -0
  14. rockycode/engine/compaction.py +181 -0
  15. rockycode/engine/container.py +225 -0
  16. rockycode/engine/effort.py +46 -0
  17. rockycode/engine/events.py +101 -0
  18. rockycode/engine/explore.py +592 -0
  19. rockycode/engine/goal.py +541 -0
  20. rockycode/engine/goal_review.py +161 -0
  21. rockycode/engine/goal_session.py +259 -0
  22. rockycode/engine/headless.py +481 -0
  23. rockycode/engine/loop.py +711 -0
  24. rockycode/engine/lsp.py +473 -0
  25. rockycode/engine/mcp.py +364 -0
  26. rockycode/engine/modes.py +123 -0
  27. rockycode/engine/outcome.py +81 -0
  28. rockycode/engine/permission.py +198 -0
  29. rockycode/engine/planmode.py +249 -0
  30. rockycode/engine/providers.py +196 -0
  31. rockycode/engine/redact.py +83 -0
  32. rockycode/engine/safety.py +139 -0
  33. rockycode/engine/sandbox.py +219 -0
  34. rockycode/engine/server.py +431 -0
  35. rockycode/engine/skills.py +178 -0
  36. rockycode/engine/titler.py +46 -0
  37. rockycode/engine/tools.py +479 -0
  38. rockycode/engine/trajectory.py +131 -0
  39. rockycode/engine/web.py +431 -0
  40. rockycode/engine/worktree.py +128 -0
  41. rockycode/memory/__init__.py +7 -0
  42. rockycode/memory/index.py +260 -0
  43. rockycode/memory/store.py +331 -0
  44. rockycode/modes/learn/learn.md +46 -0
  45. rockycode/modes/research/deep-research.md +53 -0
  46. rockycode/modes/research/paper-reading.md +49 -0
  47. rockycode/modes/research/prove.md +60 -0
  48. rockycode/modes/research/whiteboard.md +64 -0
  49. rockycode/onboarding.py +332 -0
  50. rockycode/palette.py +15 -0
  51. rockycode/pricing.py +178 -0
  52. rockycode/prompts/__init__.py +0 -0
  53. rockycode/prompts/rocky.py +257 -0
  54. rockycode/routines.py +287 -0
  55. rockycode/runners/__init__.py +0 -0
  56. rockycode/runners/agent.py +273 -0
  57. rockycode/runners/data.py +61 -0
  58. rockycode/runners/raw.py +176 -0
  59. rockycode/score.py +114 -0
  60. rockycode/session.py +298 -0
  61. rockycode/skills/architecture-viz/SKILL.md +71 -0
  62. rockycode/skills/architecture-viz/template.html +87 -0
  63. rockycode/skills/lean-prover/SKILL.md +155 -0
  64. rockycode/skills/lean-prover/torchlean-api.md +85 -0
  65. rockycode/tui/__init__.py +1 -0
  66. rockycode/tui/app.py +2450 -0
  67. rockycode/tui/exitsheet.py +181 -0
  68. rockycode/tui/goal_screen.py +315 -0
  69. rockycode/tui/mdterm.py +232 -0
  70. rockycode/tui/mdview.py +99 -0
  71. rockycode/tui/modepicker.py +103 -0
  72. rockycode/tui/permission.py +154 -0
  73. rockycode/tui/plangate.py +110 -0
  74. rockycode/tui/prompt_history.py +77 -0
  75. rockycode/tui/proposalcard.py +126 -0
  76. rockycode/tui/resume.py +142 -0
  77. rockycode/tui/rocky_pet.py +96 -0
  78. rockycode/tui/routinecard.py +123 -0
  79. rockycode-0.1.0.dist-info/METADATA +488 -0
  80. rockycode-0.1.0.dist-info/RECORD +83 -0
  81. rockycode-0.1.0.dist-info/WHEEL +4 -0
  82. rockycode-0.1.0.dist-info/entry_points.txt +2 -0
  83. rockycode-0.1.0.dist-info/licenses/LICENSE +21 -0
rockycode/config.py ADDED
@@ -0,0 +1,178 @@
1
+ """User config — persistent preferences (currency, theme, language, …).
2
+
3
+ Resolution order (later wins): built-in DEFAULTS → global
4
+ `~/.rockycode/config.toml` → project `<wd>/.rockycode/config.toml`. CLI flags
5
+ and env vars still override at runtime; this file is just for sticky
6
+ preferences you don't want to retype.
7
+
8
+ TOML for reading (stdlib tomllib) to match the ecosystem; writes are a flat
9
+ hand-rolled emit (the config is flat, so no toml-writer dependency needed).
10
+ """
11
+ from __future__ import annotations
12
+
13
+ import tomllib
14
+ from pathlib import Path
15
+ from typing import Any, Optional
16
+
17
+ GLOBAL_PATH = Path.home() / ".rockycode" / "config.toml"
18
+ PROJECT_REL = Path(".rockycode") / "config.toml"
19
+
20
+ DEFAULTS: dict[str, Any] = {
21
+ "currency": "usd", # usd | cny — which official price table to show cost in
22
+ "theme": "dark", # dark | light
23
+ # Reply language: auto mirrors whatever language the user writes in; zh
24
+ # adds a native 语言要求 block to the prompt (reasoning + replies in
25
+ # Chinese, code/identifiers untouched). Resolved once at session build.
26
+ # UI strings are en for now; zh planned on this same key.
27
+ "language": "auto", # auto | en | zh
28
+ "permission": "ask", # yolo | ask | careful — tool-approval strictness
29
+ # self-evolve is EXPERIMENTAL and ships default-OFF: nothing fires unless the
30
+ # user opts in (set exit_sheet=auto|on and dream=auto). auto = only when the
31
+ # dream pipeline is alive (memory on + Ollama reachable), so opting in still
32
+ # never nags a user without the local stack.
33
+ "exit_sheet": "off", # off | auto | on — the exit feedback sheet (auto-skips after 60s)
34
+ "dream": "manual", # manual | auto — catch-up dream pass at launch (local Ollama)
35
+ # Collaboration mode applied at launch ("" = none). Set by `/research always`.
36
+ # Resolved against BUILT-IN modes only — a cloned repo's project config must
37
+ # not auto-inject a project-local mode file into the prompt (trust rule,
38
+ # same spirit as permission never loosening).
39
+ "mode": "",
40
+ # Model limits — set these for a non-DeepSeek model (env ROCKYCODE_CONTEXT_WINDOW
41
+ # / ROCKYCODE_MAX_TOKENS and the CLI flags override at runtime).
42
+ "context_window": 1_048_576, # DeepSeek V4 = 1M; compaction acts at 50%
43
+ "max_tokens": 384_000, # per-call output cap incl. CoT (DeepSeek V4 max = 384K)
44
+ }
45
+
46
+ _ALLOWED = {
47
+ "currency": {"usd", "cny"},
48
+ "theme": {"dark", "light"},
49
+ "language": {"auto", "en", "zh"},
50
+ "permission": {"yolo", "ask", "careful"},
51
+ "exit_sheet": {"auto", "on", "off"},
52
+ "dream": {"auto", "manual"},
53
+ }
54
+
55
+
56
+ def _read(path: Path) -> dict:
57
+ if not path.exists():
58
+ return {}
59
+ try:
60
+ return tomllib.loads(path.read_text())
61
+ except (tomllib.TOMLDecodeError, OSError):
62
+ return {}
63
+
64
+
65
+ # Tool-permission strictness, ranked. A project file may TIGHTEN the guard but
66
+ # never LOOSEN it (see load()).
67
+ _PERM_RANK = {"careful": 2, "ask": 1, "yolo": 0}
68
+
69
+
70
+ def _sanitize(raw: dict, base: dict) -> dict:
71
+ """Merge *raw* onto *base*, dropping anything malformed.
72
+
73
+ Config can come from an untrusted cloned repo, so a bad value must never
74
+ crash the app or silently mean something dangerous. Unknown keys are
75
+ ignored; numeric fields are coerced (a quoted TOML "7.2" arrives as str);
76
+ enum fields must be in _ALLOWED or the base value is kept.
77
+ """
78
+ out = dict(base)
79
+ for k, v in raw.items():
80
+ if k not in DEFAULTS:
81
+ continue
82
+ default = DEFAULTS[k]
83
+ if isinstance(default, (int, float)) and not isinstance(default, bool):
84
+ try:
85
+ v = int(v) if isinstance(default, int) else float(v)
86
+ except (TypeError, ValueError):
87
+ continue
88
+ if v <= 0: # window / output must be positive
89
+ continue
90
+ if k in _ALLOWED and v not in _ALLOWED[k]:
91
+ continue
92
+ out[k] = v
93
+ return out
94
+
95
+
96
+ def load(workdir: Optional[Path] = None) -> dict:
97
+ cfg = _sanitize(_read(GLOBAL_PATH), DEFAULTS)
98
+ if workdir is not None:
99
+ proj = _sanitize(_read(workdir / PROJECT_REL), cfg)
100
+ # Security: a project config (possibly from a cloned repo) may make the
101
+ # tool-approval guard STRICTER but never weaker. Clamp any downgrade —
102
+ # otherwise a hostile repo ships permission="yolo" and runs every tool
103
+ # call unprompted. Tightening (ask→careful) is honored.
104
+ if _PERM_RANK.get(proj.get("permission"), 1) < _PERM_RANK.get(cfg["permission"], 1):
105
+ proj["permission"] = cfg["permission"]
106
+ cfg = proj
107
+ return cfg
108
+
109
+
110
+ def _coerce(key: str, raw: str) -> Any:
111
+ default = DEFAULTS.get(key)
112
+ if isinstance(default, bool):
113
+ return raw.strip().lower() in {"1", "true", "yes", "on"}
114
+ if isinstance(default, (int, float)) and not isinstance(default, bool):
115
+ try:
116
+ v = int(raw) if isinstance(default, int) else float(raw)
117
+ except ValueError:
118
+ return default
119
+ return v if v > 0 else default
120
+ return raw.strip()
121
+
122
+
123
+ def _emit(cfg: dict) -> str:
124
+ lines = []
125
+ for k, v in cfg.items():
126
+ if isinstance(v, bool):
127
+ lines.append(f"{k} = {'true' if v else 'false'}")
128
+ elif isinstance(v, (int, float)):
129
+ lines.append(f"{k} = {v}")
130
+ else:
131
+ lines.append(f'{k} = "{v}"')
132
+ return "\n".join(lines) + "\n"
133
+
134
+
135
+ def set_value(key: str, raw: str) -> tuple[Any, Optional[str]]:
136
+ """Persist key=value to the GLOBAL config. Returns (value, error)."""
137
+ if key not in DEFAULTS:
138
+ return None, f"unknown key '{key}'. known: {', '.join(sorted(DEFAULTS))}"
139
+ default = DEFAULTS[key]
140
+ if isinstance(default, int) and not isinstance(default, bool):
141
+ try:
142
+ value = int(raw)
143
+ except ValueError:
144
+ return None, f"{key} must be a positive integer (got {raw!r})"
145
+ if value <= 0:
146
+ return None, f"{key} must be positive (got {value})"
147
+ GLOBAL_PATH.parent.mkdir(parents=True, exist_ok=True)
148
+ current = _read(GLOBAL_PATH)
149
+ current[key] = value
150
+ GLOBAL_PATH.write_text(_emit(current))
151
+ return value, None
152
+ value = _coerce(key, raw)
153
+ if key in _ALLOWED and value not in _ALLOWED[key]:
154
+ return None, f"invalid value for {key}: {value!r}. allowed: {', '.join(sorted(_ALLOWED[key]))}"
155
+ GLOBAL_PATH.parent.mkdir(parents=True, exist_ok=True)
156
+ current = _read(GLOBAL_PATH)
157
+ current[key] = value
158
+ GLOBAL_PATH.write_text(_emit(current))
159
+ return value, None
160
+
161
+
162
+ def set_project_value(workdir: Path, key: str, raw: str) -> tuple[Any, Optional[str]]:
163
+ """Persist key=value to THIS folder's .rockycode/config.toml — folder-scoped
164
+ defaults like `/research always`. Same validation as set_value."""
165
+ if key not in DEFAULTS:
166
+ return None, f"unknown key '{key}'. known: {', '.join(sorted(DEFAULTS))}"
167
+ value = _coerce(key, raw)
168
+ if key in _ALLOWED and value not in _ALLOWED[key]:
169
+ return None, f"invalid value for {key}: {value!r}. allowed: {', '.join(sorted(_ALLOWED[key]))}"
170
+ path = Path(workdir) / PROJECT_REL
171
+ try:
172
+ path.parent.mkdir(parents=True, exist_ok=True)
173
+ current = _read(path)
174
+ current[key] = value
175
+ path.write_text(_emit(current))
176
+ except OSError as e:
177
+ return None, f"could not write {path}: {e}"
178
+ return value, None
@@ -0,0 +1,9 @@
1
+ """rockycode dream: idle-time memory consolidation on a local Ollama model.
2
+
3
+ M2 of docs/memory-dream.md — episode digestion, reconciliation, project
4
+ state, re-embedding. No daemon: manual `rockycode dream` now, TUI idle
5
+ trigger in M3.
6
+ """
7
+ from rockycode.dream.core import DreamReport, DreamRunner, OllamaChat
8
+
9
+ __all__ = ["DreamReport", "DreamRunner", "OllamaChat"]