gitcast 1.0.12__tar.gz → 1.0.13__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.
- {gitcast-1.0.12/gitcast.egg-info → gitcast-1.0.13}/PKG-INFO +1 -1
- {gitcast-1.0.12 → gitcast-1.0.13}/api/payload.py +65 -64
- {gitcast-1.0.12 → gitcast-1.0.13}/cli/gitcast.py +2 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/ocr.py +5 -1
- {gitcast-1.0.12 → gitcast-1.0.13}/core/trigger.py +9 -2
- {gitcast-1.0.12 → gitcast-1.0.13/gitcast.egg-info}/PKG-INFO +1 -1
- {gitcast-1.0.12 → gitcast-1.0.13}/setup.py +1 -1
- {gitcast-1.0.12 → gitcast-1.0.13}/.env.example +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/LICENSE +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/MANIFEST.in +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/README.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/ai/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/ai/formatter.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/ai/generator.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/ai/prompts.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/ai/viral_patterns.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/analytics.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/auth.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/auth_middleware.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/auth_routes.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/monitoring.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/ratelimit.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/routes.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/server.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/api/validators.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/assets/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/assets/favicon-16x16.png +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/assets/favicon-32x32.png +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/assets/favicon-64x64.png +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/assets/favicon.ico +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/assets/icon.png +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/cli/.env.example +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/cli/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/config/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/config/settings.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/capture.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/codebase_reader.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/framing.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/hotkey.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/log_stream.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/screenshot_session.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/security.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/core/tray.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/api-keys.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/article-generator.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/capture-flow.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/changelog.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/contributing.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/dashboard.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/insights.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/introduction.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/post-formats.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/providers.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/pypi-errors.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/quickstart.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/docs/sprint-mode.md +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/gitcast.egg-info/SOURCES.txt +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/gitcast.egg-info/dependency_links.txt +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/gitcast.egg-info/entry_points.txt +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/gitcast.egg-info/requires.txt +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/gitcast.egg-info/top_level.txt +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/publisher/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/publisher/clipboard.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/publisher/twitter.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/pyproject.toml +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/requirements.txt +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/setup.cfg +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/cleanup.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/engagement.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/insights.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/key_manager.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/logger.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/metrics.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/sprint.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/streak.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/supabase_client.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/storage/tone_memory.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/tests/test_keys.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/tests/test_ocr_fix.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/ui/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/ui/popup.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/ui/review.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/web/__init__.py +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/web/index.html +0 -0
- {gitcast-1.0.12 → gitcast-1.0.13}/web/landing.html +0 -0
|
@@ -121,76 +121,77 @@ def _build_user_message(
|
|
|
121
121
|
diff_text = git_diff.get("diff", "") if isinstance(git_diff, dict) else ""
|
|
122
122
|
if diff_text:
|
|
123
123
|
parts.append(f"## Git diff (recent code changes)\n```\n{diff_text.strip()}\n```")
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
"
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
124
|
+
# project narrative
|
|
125
|
+
if narrative:
|
|
126
|
+
parts.append(f"## Project context\n{narrative}")
|
|
127
|
+
|
|
128
|
+
parts.append(
|
|
129
|
+
"\nUsing the context above, generate the post now. "
|
|
130
|
+
"Follow the format and rules in the system prompt exactly."
|
|
131
|
+
)
|
|
132
|
+
|
|
133
|
+
return "\n\n".join(parts)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
# ── Sprint Mode payload ───────────────────────────────────────────────────────
|
|
137
|
+
|
|
139
138
|
def build_sprint_payload(sprint_log_entries: list) -> dict:
|
|
140
|
-
"""
|
|
141
|
-
Builds the payload for Sprint Mode — takes the full list of
|
|
142
|
-
silent captures and assembles them into one batched message
|
|
143
|
-
for the sprint summary prompt.
|
|
144
|
-
"""
|
|
145
|
-
parts = ["You have been given a log of captures from a coding sprint.\n"]
|
|
146
|
-
|
|
147
|
-
for i, entry in enumerate(sprint_log_entries, 1):
|
|
148
|
-
parts.append(f"--- Capture {i} of {len(sprint_log_entries)} ---")
|
|
149
|
-
|
|
150
|
-
if entry.get("raw_thought"):
|
|
151
|
-
parts.append(f"Developer thought: {entry['raw_thought']}")
|
|
152
|
-
|
|
153
|
-
if entry.get("git_diff"):
|
|
154
|
-
parts.append(f"Code changes:\n```\n{entry['git_diff'][:500]}\n```")
|
|
155
|
-
|
|
156
|
-
if entry.get("ocr_text"):
|
|
157
|
-
parts.append(f"Screen context: {entry['ocr_text'][:300]}")
|
|
158
|
-
|
|
159
|
-
if entry.get("timestamp"):
|
|
160
|
-
parts.append(f"Time: {entry['timestamp']}")
|
|
161
|
-
|
|
162
|
-
parts.append(
|
|
163
|
-
"\nSynthesize all of the above into a compelling sprint thread. "
|
|
164
|
-
"Follow the format and rules in the system prompt exactly."
|
|
165
|
-
)
|
|
166
|
-
|
|
167
|
-
narrative = get_project_narrative()
|
|
168
|
-
|
|
169
|
-
return {
|
|
170
|
-
"user_message": "\n\n".join(parts),
|
|
171
|
-
"format_keys": ["sprint_summary"],
|
|
172
|
-
"narrative": narrative,
|
|
173
|
-
"num_captures": len(sprint_log_entries),
|
|
174
|
-
"use_vision_fallback": False,
|
|
175
|
-
"screenshot_b64": None,
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
# ── Image encoder ─────────────────────────────────────────────────────────────
|
|
180
|
-
|
|
181
|
-
def _encode_image(image_path: str) -> str:
|
|
182
|
-
"""
|
|
183
|
-
Encodes an image file as a base64 string for the Gemini vision API.
|
|
184
|
-
Returns empty string if encoding fails.
|
|
185
|
-
"""
|
|
139
|
+
"""
|
|
140
|
+
Builds the payload for Sprint Mode — takes the full list of
|
|
141
|
+
silent captures and assembles them into one batched message
|
|
142
|
+
for the sprint summary prompt.
|
|
143
|
+
"""
|
|
144
|
+
parts = ["You have been given a log of captures from a coding sprint.\n"]
|
|
145
|
+
|
|
146
|
+
for i, entry in enumerate(sprint_log_entries, 1):
|
|
147
|
+
parts.append(f"--- Capture {i} of {len(sprint_log_entries)} ---")
|
|
148
|
+
|
|
149
|
+
if entry.get("raw_thought"):
|
|
150
|
+
parts.append(f"Developer thought: {entry['raw_thought']}")
|
|
151
|
+
|
|
152
|
+
if entry.get("git_diff"):
|
|
153
|
+
parts.append(f"Code changes:\n```\n{entry['git_diff'][:500]}\n```")
|
|
154
|
+
|
|
155
|
+
if entry.get("ocr_text"):
|
|
156
|
+
parts.append(f"Screen context: {entry['ocr_text'][:300]}")
|
|
157
|
+
|
|
158
|
+
if entry.get("timestamp"):
|
|
159
|
+
parts.append(f"Time: {entry['timestamp']}")
|
|
160
|
+
|
|
161
|
+
parts.append(
|
|
162
|
+
"\nSynthesize all of the above into a compelling sprint thread. "
|
|
163
|
+
"Follow the format and rules in the system prompt exactly."
|
|
164
|
+
)
|
|
165
|
+
|
|
166
|
+
narrative = get_project_narrative()
|
|
167
|
+
|
|
168
|
+
return {
|
|
169
|
+
"user_message": "\n\n".join(parts),
|
|
170
|
+
"format_keys": ["sprint_summary"],
|
|
171
|
+
"narrative": narrative,
|
|
172
|
+
"num_captures": len(sprint_log_entries),
|
|
173
|
+
"use_vision_fallback": False,
|
|
174
|
+
"screenshot_b64": None,
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
|
|
178
|
+
# ── Image encoder ─────────────────────────────────────────────────────────────
|
|
179
|
+
|
|
180
|
+
def _encode_image(image_path: str) -> str:
|
|
181
|
+
"""
|
|
182
|
+
Encodes an image file as a base64 string for the Gemini vision API.
|
|
183
|
+
Returns empty string if encoding fails.
|
|
184
|
+
"""
|
|
186
185
|
try:
|
|
187
|
-
|
|
186
|
+
from config.settings import BASE_DIR
|
|
187
|
+
path = Path(image_path)
|
|
188
|
+
if not path.is_absolute():
|
|
189
|
+
path = BASE_DIR / path
|
|
190
|
+
with open(path, "rb") as f:
|
|
188
191
|
return base64.b64encode(f.read()).decode("utf-8")
|
|
189
192
|
except Exception as e:
|
|
190
193
|
stream_log("Payload", "WARN", f"image encoding failed: {e}")
|
|
191
194
|
return ""
|
|
192
|
-
|
|
193
|
-
|
|
194
195
|
# ── Payload validator ─────────────────────────────────────────────────────────
|
|
195
196
|
|
|
196
197
|
def validate_payload(payload: dict) -> tuple[bool, list[str]]:
|
|
@@ -87,6 +87,8 @@ def main():
|
|
|
87
87
|
|
|
88
88
|
print("[OK] server running at http://localhost:8000")
|
|
89
89
|
print("[OK] browser opened")
|
|
90
|
+
print("\n> [OK] Gitcast daemon is listening globally.")
|
|
91
|
+
print("> Press Ctrl+Alt+S (or Ctrl+Shift+P) while coding to capture a screenshot + git diff and generate posts.\n")
|
|
90
92
|
|
|
91
93
|
from core.tray import run_tray
|
|
92
94
|
from core.trigger import on_trigger
|
|
@@ -32,7 +32,11 @@ def run_ocr(image_path: str) -> dict:
|
|
|
32
32
|
threshold = get_ocr_threshold() # default 60, set in config/settings.py
|
|
33
33
|
|
|
34
34
|
try:
|
|
35
|
-
|
|
35
|
+
from config.settings import BASE_DIR
|
|
36
|
+
path = Path(image_path)
|
|
37
|
+
if not path.is_absolute():
|
|
38
|
+
path = BASE_DIR / path
|
|
39
|
+
img = Image.open(path)
|
|
36
40
|
|
|
37
41
|
# get detailed output including confidence scores per word
|
|
38
42
|
data = pytesseract.image_to_data(
|
|
@@ -12,6 +12,8 @@ from storage.logger import log_post
|
|
|
12
12
|
import asyncio
|
|
13
13
|
import webbrowser
|
|
14
14
|
|
|
15
|
+
_browser_opened = False
|
|
16
|
+
|
|
15
17
|
|
|
16
18
|
def on_trigger():
|
|
17
19
|
"""
|
|
@@ -57,8 +59,13 @@ def on_trigger():
|
|
|
57
59
|
for k, v in variations.items():
|
|
58
60
|
print(f'[Trigger] {k}: {v[:50]}...')
|
|
59
61
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
+
global _browser_opened
|
|
63
|
+
if not _browser_opened:
|
|
64
|
+
print("[Trigger] opening review window")
|
|
65
|
+
webbrowser.open("http://localhost:8000")
|
|
66
|
+
_browser_opened = True
|
|
67
|
+
else:
|
|
68
|
+
print("> [OK] Posts generated. Refresh or check your active dashboard tab at http://localhost:8000")
|
|
62
69
|
|
|
63
70
|
def on_publish(post_text, format_key,
|
|
64
71
|
screenshot_path):
|
|
@@ -51,7 +51,7 @@ if os.path.exists(requirements_path):
|
|
|
51
51
|
|
|
52
52
|
setup(
|
|
53
53
|
name="gitcast",
|
|
54
|
-
version="1.0.
|
|
54
|
+
version="1.0.13",
|
|
55
55
|
description="git diff → published post. under 60 seconds.",
|
|
56
56
|
long_description=long_description,
|
|
57
57
|
long_description_content_type="text/markdown",
|
|
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
|
|
File without changes
|