codigest 0.4.2__tar.gz → 0.4.3__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.
- codigest-0.4.3/.github/workflows/publish.yml +51 -0
- {codigest-0.4.2 → codigest-0.4.3}/.github/workflows/tests.yml +2 -2
- {codigest-0.4.2 → codigest-0.4.3}/CITATION.cff +1 -1
- {codigest-0.4.2 → codigest-0.4.3}/PKG-INFO +3 -2
- {codigest-0.4.2 → codigest-0.4.3}/README.md +2 -1
- {codigest-0.4.2 → codigest-0.4.3}/pyproject.toml +1 -1
- {codigest-0.4.2 → codigest-0.4.3}/server.json +2 -2
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/mcp.py +11 -5
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/term.py +11 -3
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/termstore.py +15 -1
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/shell/hook.ps1 +1 -1
- {codigest-0.4.2 → codigest-0.4.3}/tests/test_termstore.py +14 -0
- {codigest-0.4.2 → codigest-0.4.3}/uv.lock +1 -1
- codigest-0.4.2/.github/workflows/publish.yml +0 -73
- {codigest-0.4.2 → codigest-0.4.3}/.gitignore +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/.python-version +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/LICENSE +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/__init__.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/__main__.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/cap.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/clean.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/diff.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/digest.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/init.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/scan.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/semdiff.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/status.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/commands/tree.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/config.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/cache.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/common.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/git_ops.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/output.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/processor.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/prompts.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/redact.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/resolver.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/runner.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/scanner.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/semdiff.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/shadow.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/shellhist.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/structure.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/tags.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/core/tokenizer.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/main.py +0 -0
- {codigest-0.4.2 → codigest-0.4.3}/src/codigest/options.py +0 -0
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
name: Publish
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
tags:
|
|
6
|
+
- 'v*'
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
pypi:
|
|
10
|
+
name: Build and publish to PyPI
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
environment: release
|
|
13
|
+
permissions:
|
|
14
|
+
id-token: write # PyPI Trusted Publishing
|
|
15
|
+
contents: read
|
|
16
|
+
steps:
|
|
17
|
+
- uses: actions/checkout@v7
|
|
18
|
+
- uses: astral-sh/setup-uv@v10.3.0
|
|
19
|
+
- run: uv python install 3.15
|
|
20
|
+
- run: uv build
|
|
21
|
+
- uses: pypa/gh-action-pypi-publish@release/v1
|
|
22
|
+
|
|
23
|
+
mcp:
|
|
24
|
+
name: Publish to the MCP registry
|
|
25
|
+
needs: pypi
|
|
26
|
+
runs-on: ubuntu-latest
|
|
27
|
+
permissions:
|
|
28
|
+
id-token: write # GitHub OIDC login for io.github.NaNIsaac/*
|
|
29
|
+
contents: read
|
|
30
|
+
steps:
|
|
31
|
+
- uses: actions/checkout@v7
|
|
32
|
+
|
|
33
|
+
- name: Set server.json version from the tag
|
|
34
|
+
run: |
|
|
35
|
+
VERSION="${GITHUB_REF_NAME#v}"
|
|
36
|
+
jq --arg v "$VERSION" '.version = $v | .packages[0].version = $v' server.json > server.tmp
|
|
37
|
+
mv server.tmp server.json
|
|
38
|
+
# The registry verifies the package on PyPI; wait until the release is visible.
|
|
39
|
+
for i in $(seq 30); do
|
|
40
|
+
curl -sf "https://pypi.org/pypi/codigest/$VERSION/json" > /dev/null && exit 0
|
|
41
|
+
sleep 10
|
|
42
|
+
done
|
|
43
|
+
echo "codigest $VERSION not visible on PyPI" && exit 1
|
|
44
|
+
|
|
45
|
+
- name: Install mcp-publisher
|
|
46
|
+
run: curl -sL https://github.com/modelcontextprotocol/registry/releases/latest/download/mcp-publisher_linux_amd64.tar.gz | tar xz mcp-publisher
|
|
47
|
+
|
|
48
|
+
- name: Publish
|
|
49
|
+
run: |
|
|
50
|
+
./mcp-publisher login github-oidc
|
|
51
|
+
./mcp-publisher publish
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: codigest
|
|
3
|
-
Version: 0.4.
|
|
3
|
+
Version: 0.4.3
|
|
4
4
|
Summary: Human-Centered Codebase Context Manager for LLMs
|
|
5
5
|
Project-URL: Homepage, https://github.com/NaNIsaac/codigest
|
|
6
6
|
Project-URL: Repository, https://github.com/NaNIsaac/codigest
|
|
@@ -238,6 +238,7 @@ cdg term # last command
|
|
|
238
238
|
cdg term -n 3 # last 3 commands
|
|
239
239
|
cdg term -i 2 # only the command from 2 runs ago
|
|
240
240
|
cdg term -f # last failed command
|
|
241
|
+
cdg term --here -n 3 # last 3 commands run in this project
|
|
241
242
|
|
|
242
243
|
cdg term path # where captures are stored (override with CODIGEST_HOME)
|
|
243
244
|
cdg term clear # delete all captures
|
|
@@ -249,7 +250,7 @@ cdg term clear # delete all captures
|
|
|
249
250
|
|
|
250
251
|
| Tool | Returns |
|
|
251
252
|
| --- | --- |
|
|
252
|
-
| `terminal_history` | Recent commands with output and exit code (`count`, `failed`) |
|
|
253
|
+
| `terminal_history` | Recent commands with output and exit code (`count`, `failed`, `here`: only this project, default on) |
|
|
253
254
|
| `scan` | Project snapshot (`targets`, `resolve`, `message`) |
|
|
254
255
|
| `diff` / `semdiff` | Text / structural changes since the anchor |
|
|
255
256
|
| `digest` | Signatures and docstrings only |
|
|
@@ -212,6 +212,7 @@ cdg term # last command
|
|
|
212
212
|
cdg term -n 3 # last 3 commands
|
|
213
213
|
cdg term -i 2 # only the command from 2 runs ago
|
|
214
214
|
cdg term -f # last failed command
|
|
215
|
+
cdg term --here -n 3 # last 3 commands run in this project
|
|
215
216
|
|
|
216
217
|
cdg term path # where captures are stored (override with CODIGEST_HOME)
|
|
217
218
|
cdg term clear # delete all captures
|
|
@@ -223,7 +224,7 @@ cdg term clear # delete all captures
|
|
|
223
224
|
|
|
224
225
|
| Tool | Returns |
|
|
225
226
|
| --- | --- |
|
|
226
|
-
| `terminal_history` | Recent commands with output and exit code (`count`, `failed`) |
|
|
227
|
+
| `terminal_history` | Recent commands with output and exit code (`count`, `failed`, `here`: only this project, default on) |
|
|
227
228
|
| `scan` | Project snapshot (`targets`, `resolve`, `message`) |
|
|
228
229
|
| `diff` / `semdiff` | Text / structural changes since the anchor |
|
|
229
230
|
| `digest` | Signatures and docstrings only |
|
|
@@ -7,12 +7,12 @@
|
|
|
7
7
|
"url": "https://github.com/NaNIsaac/codigest",
|
|
8
8
|
"source": "github"
|
|
9
9
|
},
|
|
10
|
-
"version": "0.4.
|
|
10
|
+
"version": "0.4.3",
|
|
11
11
|
"packages": [
|
|
12
12
|
{
|
|
13
13
|
"registryType": "pypi",
|
|
14
14
|
"identifier": "codigest",
|
|
15
|
-
"version": "0.4.
|
|
15
|
+
"version": "0.4.3",
|
|
16
16
|
"runtimeHint": "uvx",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
@@ -15,6 +15,7 @@ exactly what a human would paste, and stdout stays clean for the protocol.
|
|
|
15
15
|
import json
|
|
16
16
|
import subprocess
|
|
17
17
|
import sys
|
|
18
|
+
from pathlib import Path
|
|
18
19
|
from importlib.metadata import PackageNotFoundError, version
|
|
19
20
|
from typing import Any
|
|
20
21
|
|
|
@@ -40,6 +41,11 @@ TOOLS: list[JSON] = [
|
|
|
40
41
|
"properties": {
|
|
41
42
|
"count": {"type": "integer", "minimum": 1, "default": 1, "description": "How many recent commands"},
|
|
42
43
|
"failed": {"type": "boolean", "default": False, "description": "Only commands that exited non-zero"},
|
|
44
|
+
"here": {
|
|
45
|
+
"type": "boolean",
|
|
46
|
+
"default": True,
|
|
47
|
+
"description": "Only commands run in the server's working directory (the project) or below",
|
|
48
|
+
},
|
|
43
49
|
},
|
|
44
50
|
},
|
|
45
51
|
},
|
|
@@ -81,12 +87,12 @@ TOOLS: list[JSON] = [
|
|
|
81
87
|
|
|
82
88
|
def _terminal_history(args: JSON) -> str:
|
|
83
89
|
count = int(args.get("count", 1))
|
|
84
|
-
entries
|
|
85
|
-
|
|
86
|
-
entries = [e for e in entries if e.exit_code not in (None, 0)]
|
|
87
|
-
if not entries:
|
|
90
|
+
if not (entries := termstore.load_entries()):
|
|
91
|
+
# Nothing captured at all: native shell history still names the commands.
|
|
88
92
|
entries, _ = shellhist.read_recent(count)
|
|
89
|
-
|
|
93
|
+
return termstore.render(entries)
|
|
94
|
+
under = Path.cwd() if args.get("here", True) else None
|
|
95
|
+
return termstore.render(termstore.select(entries, failed=bool(args.get("failed")), under=under)[-count:])
|
|
90
96
|
|
|
91
97
|
|
|
92
98
|
def _cli(command: str, args: JSON) -> str:
|
|
@@ -40,6 +40,11 @@ FailedOption = Annotated[
|
|
|
40
40
|
typer.Option("-f", "--failed", help="Only commands that exited non-zero"),
|
|
41
41
|
]
|
|
42
42
|
|
|
43
|
+
HereOption = Annotated[
|
|
44
|
+
bool,
|
|
45
|
+
typer.Option("--here", help="Only commands run in the current directory or below"),
|
|
46
|
+
]
|
|
47
|
+
|
|
43
48
|
HistoryOnlyOption = Annotated[
|
|
44
49
|
bool,
|
|
45
50
|
typer.Option(
|
|
@@ -68,6 +73,7 @@ def handle(
|
|
|
68
73
|
count: CountOption = 1,
|
|
69
74
|
index: IndexOption = None,
|
|
70
75
|
failed: FailedOption = False,
|
|
76
|
+
here: HereOption = False,
|
|
71
77
|
history_only: HistoryOnlyOption = False,
|
|
72
78
|
copy: CopyOption = True,
|
|
73
79
|
stdout: StdoutOption = False,
|
|
@@ -80,6 +86,7 @@ def handle(
|
|
|
80
86
|
cdg term -n 3 # the last 3 commands
|
|
81
87
|
cdg term -i 3 # only the command from 3 runs ago
|
|
82
88
|
cdg term -f # the last failed command
|
|
89
|
+
cdg term --here -n 3 # the last 3 commands run in this project
|
|
83
90
|
"""
|
|
84
91
|
if ctx.invoked_subcommand:
|
|
85
92
|
return
|
|
@@ -107,10 +114,11 @@ def handle(
|
|
|
107
114
|
_diagnose(log)
|
|
108
115
|
raise typer.Exit(code=1)
|
|
109
116
|
|
|
110
|
-
if failed:
|
|
111
|
-
entries =
|
|
117
|
+
if failed or here:
|
|
118
|
+
entries = termstore.select(entries, failed=failed, under=Path.cwd() if here else None)
|
|
112
119
|
if not entries:
|
|
113
|
-
|
|
120
|
+
what = " and ".join(w for w, on in (("failed", failed), ("in this directory", here)) if on)
|
|
121
|
+
log.print(f"[yellow]No captured commands match ({what}).[/yellow]")
|
|
114
122
|
raise typer.Exit(code=1)
|
|
115
123
|
|
|
116
124
|
if index:
|
|
@@ -131,6 +131,20 @@ def _truncate(text: str) -> tuple[str, bool]:
|
|
|
131
131
|
# Read / write
|
|
132
132
|
# --------------------------------------------------------------------------
|
|
133
133
|
|
|
134
|
+
def _inside(path: str, root: Path) -> bool:
|
|
135
|
+
path, base = (os.path.normcase(os.path.abspath(p)) for p in (path, root))
|
|
136
|
+
return path == base or path.startswith(base.rstrip(os.sep) + os.sep)
|
|
137
|
+
|
|
138
|
+
|
|
139
|
+
def select(entries: list[Entry], *, failed: bool = False, under: Path | None = None) -> list[Entry]:
|
|
140
|
+
"""Keeps entries that exited non-zero (`failed`) and/or ran inside `under` or below it."""
|
|
141
|
+
if failed:
|
|
142
|
+
entries = [e for e in entries if e.exit_code not in (None, 0)]
|
|
143
|
+
if under is not None:
|
|
144
|
+
entries = [e for e in entries if e.cwd and _inside(e.cwd, under)]
|
|
145
|
+
return entries
|
|
146
|
+
|
|
147
|
+
|
|
134
148
|
def load_entries() -> list[Entry]:
|
|
135
149
|
"""Loads all stored entries, oldest first. Corrupt lines are skipped."""
|
|
136
150
|
path = get_history_path()
|
|
@@ -183,7 +197,7 @@ def _trim(path: Path) -> None:
|
|
|
183
197
|
"""Rewrites the log keeping only the most recent MAX_ENTRIES lines."""
|
|
184
198
|
with open(path, "r", encoding="utf-8", errors="replace") as f:
|
|
185
199
|
lines = f.readlines()
|
|
186
|
-
#
|
|
200
|
+
# No file lock: Concurrent hook ingests can drop an entry appended
|
|
187
201
|
# between read and replace; the slack makes rewrites (and that window) rare.
|
|
188
202
|
if len(lines) <= MAX_ENTRIES + _TRIM_SLACK:
|
|
189
203
|
return
|
|
@@ -74,7 +74,7 @@ if (-not (Get-Module PSReadLine) -and $Host.Name -eq 'ConsoleHost') {
|
|
|
74
74
|
}
|
|
75
75
|
if (-not (Get-Module PSReadLine)) { return }
|
|
76
76
|
|
|
77
|
-
#
|
|
77
|
+
# Denylist heuristic for programs that need a real TTY (editors, pagers,
|
|
78
78
|
# REPLs, ssh). Extend with $env:CODIGEST_CAP_SKIP (a regex) when one slips through.
|
|
79
79
|
$global:__cdgSkip = '^\s*(vim?|nvim|nano|emacs|less|more|ssh|htop|btop|top|fzf|lazygit|claude|codex|wsl|exit)(\.exe)?\b' +
|
|
80
80
|
'|^\s*(cdg|codigest)(\.exe)?\s+(term|mcp)\b' +
|
|
@@ -32,6 +32,20 @@ def test_mcp_lists_tools_and_ignores_notifications():
|
|
|
32
32
|
assert mcp.handle_message({"jsonrpc": "2.0", "id": 2, "method": "nope"})["error"]["code"] == -32601
|
|
33
33
|
|
|
34
34
|
|
|
35
|
+
|
|
36
|
+
def test_select_filters_failed_and_directory(tmp_path):
|
|
37
|
+
project, other = tmp_path / "proj", tmp_path / "proj-other"
|
|
38
|
+
entries = [
|
|
39
|
+
Entry(command="a", exit_code=1, cwd=str(project / "sub")),
|
|
40
|
+
Entry(command="b", exit_code=0, cwd=str(project)),
|
|
41
|
+
Entry(command="c", exit_code=2, cwd=str(other)), # shares the prefix, not the directory
|
|
42
|
+
Entry(command="d", exit_code=None, cwd=""),
|
|
43
|
+
]
|
|
44
|
+
assert [e.command for e in termstore.select(entries, under=project)] == ["a", "b"]
|
|
45
|
+
assert [e.command for e in termstore.select(entries, failed=True)] == ["a", "c"]
|
|
46
|
+
assert [e.command for e in termstore.select(entries, failed=True, under=project)] == ["a"]
|
|
47
|
+
|
|
48
|
+
|
|
35
49
|
if __name__ == "__main__":
|
|
36
50
|
test_crlf_output_survives_normalization()
|
|
37
51
|
test_progress_redraw_keeps_last_frame()
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
name: Publish Python distributions to PyPI and GitHub Pages
|
|
2
|
-
|
|
3
|
-
on:
|
|
4
|
-
push:
|
|
5
|
-
tags:
|
|
6
|
-
- 'v*' # v0.3.0 태그 푸시 시 배포 실행
|
|
7
|
-
|
|
8
|
-
jobs:
|
|
9
|
-
build-n-publish:
|
|
10
|
-
name: Build and publish Python distributions to PyPI
|
|
11
|
-
runs-on: ubuntu-latest
|
|
12
|
-
# v* 태그일 때만 실행
|
|
13
|
-
if: startsWith(github.ref, 'refs/tags/v')
|
|
14
|
-
environment: release
|
|
15
|
-
permissions:
|
|
16
|
-
id-token: write # Trusted Publishing을 위한 필수 권한
|
|
17
|
-
contents: read
|
|
18
|
-
|
|
19
|
-
steps:
|
|
20
|
-
- uses: actions/checkout@v4
|
|
21
|
-
|
|
22
|
-
# uv 설치 (가장 빠르고 확실한 방법)
|
|
23
|
-
- name: Install uv
|
|
24
|
-
uses: astral-sh/setup-uv@v5
|
|
25
|
-
|
|
26
|
-
# Python 3.14 설정 (uv 이용)
|
|
27
|
-
- name: Set up Python
|
|
28
|
-
run: uv python install 3.15
|
|
29
|
-
|
|
30
|
-
# 의존성 설치 및 빌드 (uv build 하나로 해결)
|
|
31
|
-
- name: Build package
|
|
32
|
-
run: uv build
|
|
33
|
-
|
|
34
|
-
# PyPI 배포 (기존 형식 유지)
|
|
35
|
-
- name: Publish package distributions to PyPI
|
|
36
|
-
uses: pypa/gh-action-pypi-publish@release/v1
|
|
37
|
-
|
|
38
|
-
deploy-docs:
|
|
39
|
-
name: Build and deploy Sphinx docs to GitHub Pages
|
|
40
|
-
runs-on: ubuntu-latest
|
|
41
|
-
if: github.ref == 'refs/heads/main'
|
|
42
|
-
permissions:
|
|
43
|
-
contents: write
|
|
44
|
-
steps:
|
|
45
|
-
- uses: actions/checkout@v4
|
|
46
|
-
|
|
47
|
-
- name: Install uv
|
|
48
|
-
uses: astral-sh/setup-uv@v5
|
|
49
|
-
|
|
50
|
-
- name: Set up Python
|
|
51
|
-
run: uv python install 3.15
|
|
52
|
-
|
|
53
|
-
- name: Install Python and Sphinx dependencies
|
|
54
|
-
run: |
|
|
55
|
-
uv pip install --system --upgrade pip
|
|
56
|
-
if [ -f docs/requirements.txt ]; then uv pip install --system -r docs/requirements.txt; fi
|
|
57
|
-
|
|
58
|
-
- name: Build HTML docs with Sphinx
|
|
59
|
-
run: |
|
|
60
|
-
if [ -d "docs" ]; then
|
|
61
|
-
sphinx-build -b html docs/source docs/html
|
|
62
|
-
touch docs/html/.nojekyll
|
|
63
|
-
else
|
|
64
|
-
echo "No docs folder found, skipping build."
|
|
65
|
-
mkdir -p docs/html
|
|
66
|
-
touch docs/html/.nojekyll
|
|
67
|
-
fi
|
|
68
|
-
|
|
69
|
-
- name: Deploy to GitHub Pages
|
|
70
|
-
uses: peaceiris/actions-gh-pages@v3
|
|
71
|
-
with:
|
|
72
|
-
github_token: ${{ secrets.GITHUB_TOKEN }}
|
|
73
|
-
publish_dir: docs/html
|
|
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
|