sin-code-bundle 0.9.2__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 (41) hide show
  1. sin_code_bundle/__init__.py +6 -0
  2. sin_code_bundle/agents_md.py +245 -0
  3. sin_code_bundle/ast_edit.py +323 -0
  4. sin_code_bundle/bench.py +506 -0
  5. sin_code_bundle/budget.py +51 -0
  6. sin_code_bundle/cache.py +131 -0
  7. sin_code_bundle/checkpoint.py +230 -0
  8. sin_code_bundle/cli.py +1943 -0
  9. sin_code_bundle/codocs.py +328 -0
  10. sin_code_bundle/dap_bridge.py +135 -0
  11. sin_code_bundle/data/codocs/SKILL.md +280 -0
  12. sin_code_bundle/gitnexus.py +368 -0
  13. sin_code_bundle/hashline.py +216 -0
  14. sin_code_bundle/hooks.py +249 -0
  15. sin_code_bundle/immortal_commit.py +288 -0
  16. sin_code_bundle/interceptor.py +119 -0
  17. sin_code_bundle/lsp_backend.py +303 -0
  18. sin_code_bundle/lsp_bootstrap.py +85 -0
  19. sin_code_bundle/markitdown.py +254 -0
  20. sin_code_bundle/mcp_config.py +455 -0
  21. sin_code_bundle/mcp_server.py +963 -0
  22. sin_code_bundle/memory.py +208 -0
  23. sin_code_bundle/merge_safety.py +313 -0
  24. sin_code_bundle/orchestration_worktrees.py +102 -0
  25. sin_code_bundle/policy.py +224 -0
  26. sin_code_bundle/preflight.py +152 -0
  27. sin_code_bundle/programming_workflow.py +541 -0
  28. sin_code_bundle/rtk.py +154 -0
  29. sin_code_bundle/safety.py +52 -0
  30. sin_code_bundle/session_warmup.py +247 -0
  31. sin_code_bundle/skills.py +188 -0
  32. sin_code_bundle/symbol_resolve.py +166 -0
  33. sin_code_bundle/tools/__init__.py +4 -0
  34. sin_code_bundle/tools/pypi_setup.py +289 -0
  35. sin_code_bundle/vfs.py +264 -0
  36. sin_code_bundle-0.9.2.dist-info/METADATA +470 -0
  37. sin_code_bundle-0.9.2.dist-info/RECORD +41 -0
  38. sin_code_bundle-0.9.2.dist-info/WHEEL +5 -0
  39. sin_code_bundle-0.9.2.dist-info/entry_points.txt +4 -0
  40. sin_code_bundle-0.9.2.dist-info/licenses/LICENSE +21 -0
  41. sin_code_bundle-0.9.2.dist-info/top_level.txt +1 -0
@@ -0,0 +1,328 @@
1
+ # SPDX-License-Identifier: MIT
2
+ """CoDocs — Co-located Docs Standard validator.
3
+
4
+ Each code file may declare a companion ``.doc.md`` file via a first-line
5
+ reference comment, e.g.::
6
+
7
+ # Docs: router.doc.md (Python, shell, YAML, Makefile, ...)
8
+ // Docs: types.doc.md (TypeScript, Rust, Go, C, ...)
9
+
10
+ This module finds those references and verifies the referenced doc file
11
+ actually exists next to the source file. It replaces the original fragile
12
+ ``grep | sed`` one-liner with a robust, testable implementation that ignores
13
+ matches inside multi-line strings/heredocs by only inspecting the first
14
+ non-shebang lines of each file.
15
+
16
+ It is intentionally dependency-free (stdlib only) so it works even when the
17
+ optional SIN-Code subsystems are not installed.
18
+
19
+ Docs: codocs.doc.md
20
+ """
21
+
22
+ from __future__ import annotations
23
+
24
+ import re
25
+ from dataclasses import dataclass
26
+ from pathlib import Path
27
+
28
+ # ── Scanner Configuration ────────────────────────────────────────────
29
+
30
+ # Directories never scanned. Mirrors common build/VCS/tooling caches so the
31
+ # scanner stays fast on large repos (these folders balloon quickly).
32
+ DEFAULT_EXCLUDE = {
33
+ ".git",
34
+ ".hg",
35
+ ".svn",
36
+ "__pycache__",
37
+ "node_modules",
38
+ "venv",
39
+ ".venv",
40
+ "dist",
41
+ "build",
42
+ ".mypy_cache",
43
+ ".pytest_cache",
44
+ ".ruff_cache",
45
+ }
46
+
47
+ # File extensions we consider "code" and therefore eligible for a Docs: ref.
48
+ # Limited to languages the SIN-Code stack actively targets; add carefully
49
+ # because the regex below is tuned to C-style/line/Python comment leaders.
50
+ # Makefile and Dockerfile are matched by name in ``_is_code_file`` (no suffix).
51
+ CODE_SUFFIXES = {
52
+ ".py",
53
+ ".pyi",
54
+ ".ts",
55
+ ".tsx",
56
+ ".js",
57
+ ".jsx",
58
+ ".mjs",
59
+ ".cjs",
60
+ ".rs",
61
+ ".go",
62
+ ".java",
63
+ ".kt",
64
+ ".kts",
65
+ ".scala",
66
+ ".c",
67
+ ".h",
68
+ ".cc",
69
+ ".cpp",
70
+ ".hpp",
71
+ ".cs",
72
+ ".rb",
73
+ ".php",
74
+ ".swift",
75
+ ".sh",
76
+ ".bash",
77
+ ".zsh",
78
+ ".yaml",
79
+ ".yml",
80
+ ".toml",
81
+ }
82
+
83
+ # Extensionless files that still count as code (matched by exact basename).
84
+ CODE_FILENAMES = {"Makefile", "Dockerfile", "Justfile"}
85
+
86
+ # How many leading lines to inspect for a reference. The standard places it on
87
+ # the first line; we allow a small window (5) to tolerate a shebang / encoding
88
+ # cookie / license header line above it. Keep small — false positives grow
89
+ # linearly with this value.
90
+ _HEAD_LINES = 5
91
+
92
+ # Matches: optional comment leader, then "Docs:" then a path ending in .doc.md.
93
+ # Regex is VERBOSE so the comment leaders are easy to extend when new
94
+ # languages are added. The final `\*?/?\s*$` swallows closing block-comment
95
+ # tokens like `*/` so ``/* Docs: foo.doc.md */`` matches.
96
+ _DOCS_RE = re.compile(
97
+ r"""^\s*
98
+ (?:\#|//|/\*|\*|--|;)? # optional comment leader
99
+ \s*Docs:\s*
100
+ (?P<doc>[^\s*]+?\.doc\.md) # the referenced doc path
101
+ \s*\*?/?\s*$ # optional closing comment
102
+ """,
103
+ re.VERBOSE,
104
+ )
105
+
106
+
107
+ # ── CoDocsReference: Parsed Reference ────────────────────────────────
108
+
109
+
110
+ @dataclass(frozen=True)
111
+ class DocReference:
112
+ """A ``Docs:`` reference discovered in a source file.
113
+
114
+ Attributes:
115
+ source: Path of the code file containing the reference, relative to
116
+ the scan root.
117
+ doc: The raw referenced path as written in the source (e.g.
118
+ ``"router.doc.md"``). Unvalidated.
119
+ resolved: Absolute path the reference resolves to (source parent +
120
+ doc), computed at scan time.
121
+ exists: Whether ``resolved`` points to a regular file on disk.
122
+ """
123
+
124
+ source: Path
125
+ doc: str # raw referenced path, as written
126
+ resolved: Path # absolute path the reference resolves to
127
+ exists: bool
128
+
129
+ def to_dict(self) -> dict:
130
+ """Serialize to a JSON-friendly dict for CLI/JSON output."""
131
+ return {
132
+ "source": str(self.source),
133
+ "doc": self.doc,
134
+ "resolved": str(self.resolved),
135
+ "exists": self.exists,
136
+ }
137
+
138
+
139
+ # ── Scanner: Find All # Docs: References ──────────────────────────────
140
+
141
+
142
+ def _is_code_file(path: Path) -> bool:
143
+ """True if ``path`` is a code file eligible for CoDocs scanning."""
144
+ if path.name in CODE_FILENAMES:
145
+ return True
146
+ return path.suffix in CODE_SUFFIXES
147
+
148
+
149
+ def _iter_code_files(root: Path, exclude: set[str]):
150
+ """Yield eligible code files under ``root``, skipping ``exclude`` dirs."""
151
+ for path in sorted(root.rglob("*")):
152
+ if not path.is_file():
153
+ continue
154
+ if any(part in exclude for part in path.parts):
155
+ continue
156
+ if _is_code_file(path):
157
+ yield path
158
+
159
+
160
+ def _extract_reference(path: Path) -> str | None:
161
+ """Return the referenced ``.doc.md`` path from a file's head, or None.
162
+
163
+ Reads at most ``_HEAD_LINES`` lines (shebang/encoding tolerant). Returns
164
+ None on either "no match" or "file unreadable" so the scanner keeps going.
165
+ """
166
+ try:
167
+ with path.open("r", encoding="utf-8", errors="ignore") as fh:
168
+ for _ in range(_HEAD_LINES):
169
+ line = fh.readline()
170
+ if line == "":
171
+ break
172
+ match = _DOCS_RE.match(line)
173
+ if match:
174
+ return match.group("doc")
175
+ except OSError:
176
+ # Permission denied, binary file, etc. — treat as "no reference" so
177
+ # one bad file does not abort the whole scan.
178
+ return None
179
+ return None
180
+
181
+
182
+ # ── Validation: Check References Resolve ──────────────────────────────
183
+
184
+
185
+ def scan(root: str | Path = ".", exclude: set[str] | None = None) -> list[DocReference]:
186
+ """Scan ``root`` and return every CoDocs reference found.
187
+
188
+ Walks the tree, reads each code file's head, parses the ``Docs:`` line,
189
+ and resolves the target relative to the source file's directory. Files
190
+ without a reference are skipped silently; unreachable references are
191
+ still returned with ``exists=False`` so callers can report them.
192
+
193
+ Args:
194
+ root: Filesystem path to scan. Defaults to current working directory.
195
+ exclude: Additional directory basenames to skip (merged with
196
+ ``DEFAULT_EXCLUDE``).
197
+
198
+ Returns:
199
+ A list of ``DocReference`` (one per file that declares a Docs line),
200
+ sorted by source path.
201
+ """
202
+ root_path = Path(root).resolve()
203
+ excl = DEFAULT_EXCLUDE | (exclude or set())
204
+ references: list[DocReference] = []
205
+ for source in _iter_code_files(root_path, excl):
206
+ doc = _extract_reference(source)
207
+ if doc is None:
208
+ continue
209
+ resolved = (source.parent / doc).resolve()
210
+ references.append(
211
+ DocReference(
212
+ source=source.relative_to(root_path),
213
+ doc=doc,
214
+ resolved=resolved,
215
+ exists=resolved.is_file(),
216
+ )
217
+ )
218
+ return references
219
+
220
+
221
+ def find_broken(root: str | Path = ".", exclude: set[str] | None = None) -> list[DocReference]:
222
+ """Return only the references whose target doc file is missing."""
223
+ return [ref for ref in scan(root, exclude) if not ref.exists]
224
+
225
+
226
+ # ── SOTA Inline Doc checks ─────────────────────────────────────────────
227
+
228
+
229
+ @dataclass(frozen=True)
230
+ class InlineDocIssue:
231
+ """A missing or deficient inline doc element.
232
+
233
+ Attributes:
234
+ path: Source file the issue was found in, relative to the scan root.
235
+ kind: Machine-readable issue category. Currently one of:
236
+ ``"missing_purpose"`` — file lacks a Purpose/module-docstring
237
+ header in its first few lines.
238
+ detail: Human-readable explanation suitable for CLI output.
239
+ """
240
+
241
+ path: Path
242
+ kind: str # "missing_purpose", "missing_docstring", "missing_section"
243
+ detail: str
244
+
245
+ def to_dict(self) -> dict:
246
+ """Serialize to a JSON-friendly dict for CLI/JSON output."""
247
+ return {"path": str(self.path), "kind": self.kind, "detail": self.detail}
248
+
249
+
250
+ # Detects a SOTA-compliant file header: ``# Purpose: ...`` line or a
251
+ # Python module docstring (triple-single or triple-double quotes) appearing
252
+ # in the first _HEAD_LINES lines of the file.
253
+ _INLINE_HEAD_RE = re.compile(r"^\s*(?:#\s*Purpose\s*:|'''|\"\"\")")
254
+
255
+
256
+ def check_inline_docs(
257
+ root: str | Path = ".",
258
+ exclude: set[str] | None = None,
259
+ ) -> list[InlineDocIssue]:
260
+ """Check files for SOTA inline doc compliance.
261
+
262
+ Currently checks:
263
+ - File header with ``Purpose`` line or module docstring.
264
+
265
+ The check is intentionally narrow: false positives in a docs linter
266
+ create noise, so we only flag files with no Purpose line AND no
267
+ Python module docstring in the first ``_HEAD_LINES`` lines.
268
+
269
+ Args:
270
+ root: Filesystem path to scan. Defaults to current working directory.
271
+ exclude: Additional directory basenames to skip (merged with
272
+ ``DEFAULT_EXCLUDE`` plus ``debug``/``tmp``).
273
+
274
+ Returns:
275
+ A list of ``InlineDocIssue`` (one per non-compliant file), sorted by
276
+ path.
277
+ """
278
+ root_path = Path(root).resolve()
279
+ # ``debug``/``tmp`` are extra ignores on top of the standard excludes —
280
+ # these folders are explicitly for throwaway experiments and are exempt
281
+ # from the docs standard per the AGENTS.md exception list.
282
+ excl = DEFAULT_EXCLUDE | {"debug", "tmp"} | (exclude or set())
283
+ issues: list[InlineDocIssue] = []
284
+
285
+ for path in sorted(root_path.rglob("*")):
286
+ if not path.is_file():
287
+ continue
288
+ if any(part in excl for part in path.parts):
289
+ continue
290
+ if not _is_code_file(path):
291
+ continue
292
+ # Inline doc header is currently only defined for the languages with
293
+ # line comments or Python module docstrings. YAML/TOML/SH etc. are
294
+ # scanned for Docs: refs but not for inline headers.
295
+ if path.suffix not in (".py", ".pyi", ".ts", ".tsx", ".js", ".jsx", ".rs", ".go"):
296
+ continue
297
+
298
+ try:
299
+ text = path.read_text(encoding="utf-8", errors="ignore")
300
+ except OSError:
301
+ continue
302
+
303
+ head = "\n".join(text.splitlines()[:_HEAD_LINES])
304
+ rel = path.relative_to(root_path)
305
+ if not _INLINE_HEAD_RE.search(head):
306
+ issues.append(
307
+ InlineDocIssue(
308
+ path=rel,
309
+ kind="missing_purpose",
310
+ detail="Missing Purpose/header comment in first lines",
311
+ )
312
+ )
313
+
314
+ return issues
315
+
316
+
317
+ def _check_inline_docs_json(root: str = ".", exclude: set[str] | None = None) -> str:
318
+ """Inline doc check as JSON string, for CLI use.
319
+
320
+ Wraps :func:`check_inline_docs` in a JSON serialization so the CLI can
321
+ pipe the result without each caller re-importing :mod:`json`.
322
+ """
323
+ import json
324
+
325
+ return json.dumps(
326
+ [issue.to_dict() for issue in check_inline_docs(root, exclude)],
327
+ indent=2,
328
+ )
@@ -0,0 +1,135 @@
1
+ """Purpose: DAP runtime bridge for SIN-Code — attach debuggers, store runtime facts.
2
+
3
+ Docs: dap_bridge.doc.md
4
+ """
5
+
6
+ from __future__ import annotations
7
+
8
+ import subprocess
9
+ from pathlib import Path
10
+ from typing import Optional
11
+
12
+
13
+ class DAPSession:
14
+ """Manages a single DAP debugging session."""
15
+
16
+ def __init__(self, language: str, target: str, repo_root: Path):
17
+ self.language = language
18
+ self.target = target
19
+ self.repo_root = repo_root
20
+ self.process: Optional[subprocess.Popen] = None
21
+ self.port: Optional[int] = None
22
+
23
+ def start(self) -> dict:
24
+ try:
25
+ if self.language == "python":
26
+ self.port = 5678 # debugpy default port (https://github.com/microsoft/debugpy)
27
+ self.process = subprocess.Popen(
28
+ [
29
+ "python",
30
+ "-m",
31
+ "debugpy",
32
+ "--listen",
33
+ str(self.port),
34
+ "--wait-for-client",
35
+ self.target,
36
+ ],
37
+ cwd=self.repo_root,
38
+ stdout=subprocess.PIPE,
39
+ stderr=subprocess.PIPE,
40
+ )
41
+ elif self.language == "go":
42
+ self.port = 2345 # delve default headless port
43
+ self.process = subprocess.Popen(
44
+ [
45
+ "dlv",
46
+ "debug",
47
+ "--headless",
48
+ "--listen",
49
+ f":{self.port}",
50
+ "--api-version=2",
51
+ self.target,
52
+ ],
53
+ cwd=self.repo_root,
54
+ stdout=subprocess.PIPE,
55
+ stderr=subprocess.PIPE,
56
+ )
57
+ elif self.language in ("node", "javascript", "typescript"):
58
+ self.port = 9229 # node --inspect default port
59
+ self.process = subprocess.Popen(
60
+ ["node", f"--inspect-brk={self.port}", self.target],
61
+ cwd=self.repo_root,
62
+ stdout=subprocess.PIPE,
63
+ stderr=subprocess.PIPE,
64
+ )
65
+ else:
66
+ return {"error": f"Unsupported language for DAP: {self.language}"}
67
+ return {
68
+ "success": True,
69
+ "port": self.port,
70
+ "message": f"Debugger attached on port {self.port}",
71
+ }
72
+ except FileNotFoundError:
73
+ return {"error": f"Debugger for {self.language} not found (install debugpy/dlv/node)."}
74
+ except Exception as e:
75
+ return {"error": str(e)}
76
+
77
+ def stop(self) -> None:
78
+ if self.process:
79
+ try:
80
+ self.process.terminate()
81
+ except Exception:
82
+ pass
83
+ self.process = None
84
+
85
+
86
+ # ── SINRuntimeTrace: High-level Orchestrator ───────────────────────────────
87
+ class SINRuntimeTrace:
88
+ """High-level runtime tracing orchestrator."""
89
+
90
+ def __init__(self, repo_root: Optional[Path] = None):
91
+ self.repo_root = repo_root or Path.cwd()
92
+ self.sessions: dict[str, DAPSession] = {}
93
+
94
+ def trace_function(
95
+ self,
96
+ file_path: str,
97
+ function_name: str,
98
+ language: str = "python",
99
+ store_in_memory: bool = True,
100
+ ) -> dict:
101
+ session_id = f"{language}_{function_name}"
102
+ session = DAPSession(language, file_path, self.repo_root)
103
+ result = session.start()
104
+ if not result.get("success"):
105
+ return result
106
+ self.sessions[session_id] = session
107
+ if store_in_memory:
108
+ try:
109
+ from sin_code_bundle import memory
110
+
111
+ memory.remember(
112
+ f"Runtime trace initiated for {function_name} in {file_path} on port {result['port']}",
113
+ kind="runtime",
114
+ scope="repo",
115
+ )
116
+ except Exception:
117
+ pass
118
+ return {
119
+ "success": True,
120
+ "session_id": session_id,
121
+ "port": result["port"],
122
+ "message": f"Attach DAP client to localhost:{result['port']} to inspect {function_name}",
123
+ }
124
+
125
+ def get_session_status(self, session_id: str) -> dict:
126
+ if session_id in self.sessions:
127
+ return {"active": True, "port": self.sessions[session_id].port}
128
+ return {"active": False, "error": "Session not found"}
129
+
130
+ def stop_trace(self, session_id: str) -> dict:
131
+ if session_id in self.sessions:
132
+ self.sessions[session_id].stop()
133
+ del self.sessions[session_id]
134
+ return {"success": True, "message": "Session terminated"}
135
+ return {"error": "Session not found"}