@yadsh/dsh-git-readonly 0.1.0 → 0.1.1
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.
- package/README.md +3 -1
- package/package.json +5 -5
- package/SPEC.md +0 -160
package/README.md
CHANGED
|
@@ -43,8 +43,10 @@ branch → history → blame → commit — without ever receiving a command lin
|
|
|
43
43
|
|
|
44
44
|
## Installation
|
|
45
45
|
|
|
46
|
+
Install the published npm package by name:
|
|
47
|
+
|
|
46
48
|
```bash
|
|
47
|
-
dsh plugin add @yadsh/dsh-git-readonly
|
|
49
|
+
dsh plugin --profile <profile> add @yadsh/dsh-git-readonly
|
|
48
50
|
```
|
|
49
51
|
|
|
50
52
|
The plugin has no web-client surface; a host restart (or bundle reload)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@yadsh/dsh-git-readonly",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.1",
|
|
4
4
|
"description": "Read-only git provenance tools for DeepSeek Harness agents",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -27,7 +27,6 @@
|
|
|
27
27
|
"cordis.patch.yml",
|
|
28
28
|
"compatibility.json",
|
|
29
29
|
"README.md",
|
|
30
|
-
"SPEC.md",
|
|
31
30
|
"LICENSE"
|
|
32
31
|
],
|
|
33
32
|
"dsh": {
|
|
@@ -36,7 +35,7 @@
|
|
|
36
35
|
}
|
|
37
36
|
},
|
|
38
37
|
"dependencies": {
|
|
39
|
-
"@yadsh/dsh-plugin-log": "^0.
|
|
38
|
+
"@yadsh/dsh-plugin-log": "^0.3.0"
|
|
40
39
|
},
|
|
41
40
|
"peerDependencies": {
|
|
42
41
|
"@deepseek-ai/dsh-tools": ">=0.1.5-rc.2 <0.2.0",
|
|
@@ -49,8 +48,8 @@
|
|
|
49
48
|
"eslint": "10.9.1",
|
|
50
49
|
"typescript": "^7.0.2",
|
|
51
50
|
"vitest": "^4.1.11",
|
|
52
|
-
"@yadsh/dsh-
|
|
53
|
-
"@yadsh/dsh-
|
|
51
|
+
"@yadsh/dsh-config": "^0.0.0",
|
|
52
|
+
"@yadsh/dsh-test-kit": "^0.1.0"
|
|
54
53
|
},
|
|
55
54
|
"publishConfig": {
|
|
56
55
|
"access": "public",
|
|
@@ -64,6 +63,7 @@
|
|
|
64
63
|
"deepseek-harness",
|
|
65
64
|
"dsh",
|
|
66
65
|
"dsh-plugin",
|
|
66
|
+
"cordis",
|
|
67
67
|
"git",
|
|
68
68
|
"read-only",
|
|
69
69
|
"provenance",
|
package/SPEC.md
DELETED
|
@@ -1,160 +0,0 @@
|
|
|
1
|
-
# SPEC: @yadsh/dsh-git-readonly
|
|
2
|
-
|
|
3
|
-
Read-only git provenance tools for DeepSeek Harness agents.
|
|
4
|
-
|
|
5
|
-
## 1. Product contract
|
|
6
|
-
|
|
7
|
-
1. An agent whose session has a working directory can learn, in one tool
|
|
8
|
-
call, which repository it is in (`dsh_git_context`): work-tree root,
|
|
9
|
-
branch or detached state, upstream, HEAD id, last commit author/date and
|
|
10
|
-
subject, and decorated refs.
|
|
11
|
-
2. An agent can find the commit that introduced or removed a literal string
|
|
12
|
-
(`dsh_git_history` with `search: "content"`), locate commits by
|
|
13
|
-
commit-message substring, author, path or ref scope, and page through
|
|
14
|
-
the results with an explicit `truncated` marker.
|
|
15
|
-
3. An agent can attribute any line of a repository file to the commit that
|
|
16
|
-
last changed it (`dsh_git_blame`), optionally in a line window and at a
|
|
17
|
-
historical revision, and then inspect that commit (`dsh_git_show`):
|
|
18
|
-
metadata, parents, per-file line counts, and a bounded patch.
|
|
19
|
-
4. The model never supplies a command line, a git option, or a git
|
|
20
|
-
subcommand. Every argument is one of: a hexadecimal commit id
|
|
21
|
-
(`/^[0-9a-fA-F]{7,64}$/`), a repository-relative path without `..`
|
|
22
|
-
segments, a bounded literal search string, or a numeric limit.
|
|
23
|
-
5. The commit passed to `dsh_git_show` / `dsh_git_blame` is canonicalized
|
|
24
|
-
by git itself (`rev-parse --verify --end-of-options <oid>^{commit}`)
|
|
25
|
-
before use; only commits that exist resolve.
|
|
26
|
-
6. No tool call mutates the repository: refs, index, config and work tree
|
|
27
|
-
are byte-identical before and after every call (proven by tests, §9).
|
|
28
|
-
7. A hostile repository cannot execute helpers through a read:
|
|
29
|
-
`diff.external`, textconv drivers and `core.fsmonitor` configured in the
|
|
30
|
-
inspected repository are never invoked (proven by tests, §9).
|
|
31
|
-
8. A poisoned ambient environment (`GIT_DIR`, `GIT_WORK_TREE`,
|
|
32
|
-
`GIT_INDEX_FILE`, `GIT_OBJECT_DIRECTORY`, `GIT_EXTERNAL_DIFF`,
|
|
33
|
-
`GIT_CONFIG_*`, askpass/ssh keys) cannot redirect a read; those keys are
|
|
34
|
-
removed per invocation and deterministic values are forced.
|
|
35
|
-
9. Without a session working directory, or outside a git work tree, every
|
|
36
|
-
tool fails closed with a typed error; there is no fallback to the host
|
|
37
|
-
process cwd.
|
|
38
|
-
10. One call is bounded: timeouts (default 15 s, hard clamp 1–30 s), byte
|
|
39
|
-
caps for patches (default 200 KiB) and stdout, page sizes for history
|
|
40
|
-
(default 20, max 100) and blame lines (default 300). Exceeding a cap
|
|
41
|
-
yields `truncated: true`, never a hang or an unbounded answer.
|
|
42
|
-
|
|
43
|
-
## 2. Surface
|
|
44
|
-
|
|
45
|
-
| Tool | Parameters (model-facing) | Result |
|
|
46
|
-
| --- | --- | --- |
|
|
47
|
-
| `dsh_git_context` | — | root, branch?, detached, upstream?, head?, shortOid?, author?, authoredAt?, subject?, refs[] |
|
|
48
|
-
| `dsh_git_history` | path?, query?, search?, author?, limit?, offset?, all? | commits[] {oid, shortOid, author, authoredAt, subject, refs[]}, count, truncated |
|
|
49
|
-
| `dsh_git_show` | oid (required), path?, statOnly? | oid, shortOid, author, authoredAt, subject, body, parents[], files[] {path, additions?, deletions?}, patch, patchTruncated |
|
|
50
|
-
| `dsh_git_blame` | file (required), fromLine?, toLine?, oid? | file, fromLine, toLine, lines[] {line, oid, author, authoredAt, summary, content}, truncated |
|
|
51
|
-
|
|
52
|
-
Errors carry stable codes: `no-session-cwd`, `not-a-git-repository`,
|
|
53
|
-
`invalid-oid`, `invalid-path`, `invalid-argument`, `git-timeout`,
|
|
54
|
-
`git-failed`.
|
|
55
|
-
|
|
56
|
-
## 3. Data flow
|
|
57
|
-
|
|
58
|
-
```
|
|
59
|
-
model → tools.execute(args, exec)
|
|
60
|
-
│ validateCommitOid / validateRepoRelativePath / validateSearchLiteral
|
|
61
|
-
│ requireSessionCwd(exec.agent.session.header.cwd) ← fail-closed
|
|
62
|
-
│ resolveRepositoryRoot: git rev-parse --show-toplevel
|
|
63
|
-
▼
|
|
64
|
-
runGit(argv, {cwd, timeoutMs, maxBytes})
|
|
65
|
-
│ argv = [program, …prefix, --no-pager, -c core.fsmonitor=false,
|
|
66
|
-
│ -c core.quotepath=false, …subcommand]
|
|
67
|
-
│ env = parent − removed git keys + forced deterministic values
|
|
68
|
-
▼
|
|
69
|
-
git ──byte-capped stdout──▶ parsers (--format + %x1f, numstat,
|
|
70
|
-
porcelain) ──▶ structured result
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
## 4. Trust boundaries
|
|
74
|
-
|
|
75
|
-
- **Repository content is untrusted data.** Commit messages, branch names,
|
|
76
|
-
patches and file content are returned as data; every tool description
|
|
77
|
-
tells the model so. The plugin never parses untrusted output into
|
|
78
|
-
behavior.
|
|
79
|
-
- **Repository configuration is untrusted.** `.git/config` and
|
|
80
|
-
`.gitattributes` of the inspected repo may point diff/textconv/fsmonitor
|
|
81
|
-
at executables; the hardening flags disable all three paths, and §9
|
|
82
|
-
proves it.
|
|
83
|
-
- **The parent environment is untrusted.** Ambient git redirection keys are
|
|
84
|
-
stripped per invocation (§1.8).
|
|
85
|
-
|
|
86
|
-
## 5. Configuration
|
|
87
|
-
|
|
88
|
-
See the README configuration table. All limits are clamped into safe
|
|
89
|
-
corridors by `resolveGitReadonlyConfig`; `enabled: false` removes the tool
|
|
90
|
-
surface entirely instead of registering inert tools.
|
|
91
|
-
|
|
92
|
-
## 6. Hardening invariants (runner)
|
|
93
|
-
|
|
94
|
-
1. argv array only; no `shell:`; `windowsHide`; stdin ignored.
|
|
95
|
-
2. Prefix flags on every invocation: `--no-pager`, `-c core.fsmonitor=false`,
|
|
96
|
-
`-c core.quotepath=false`.
|
|
97
|
-
3. Diff-producing commands add `--no-ext-diff --no-textconv --no-color`
|
|
98
|
-
(`dsh_git_show`, and `dsh_git_history` because pickaxe runs the diff
|
|
99
|
-
machinery); `dsh_git_blame` adds `--no-textconv` (blame honors textconv
|
|
100
|
-
by default).
|
|
101
|
-
4. Environment: `REMOVED_ENV_KEYS` stripped; `LC_ALL=C`,
|
|
102
|
-
`GIT_OPTIONAL_LOCKS=0`, `GIT_TERMINAL_PROMPT=0`, `GIT_CONFIG_NOSYSTEM=1`,
|
|
103
|
-
`GIT_PAGER=cat`, `PAGER=cat` forced.
|
|
104
|
-
5. The child is killed at the byte cap or the timeout (with SIGKILL
|
|
105
|
-
escalation) and the result reports `truncated` / `timedOut`.
|
|
106
|
-
|
|
107
|
-
## 7. Lifecycle
|
|
108
|
-
|
|
109
|
-
The plugin is stateless: `apply` resolves config and registers four tools;
|
|
110
|
-
registrations are collected by the Cordis plugin scope on dispose/reload.
|
|
111
|
-
Nothing is stored on disk beyond plugin log records (tool names, exit
|
|
112
|
-
codes, durations, truncation flags — never content).
|
|
113
|
-
|
|
114
|
-
## 8. Scope
|
|
115
|
-
|
|
116
|
-
**Included:** the four provenance tools, validation, hardening, structured
|
|
117
|
-
bounded output, typed errors, mutation tests.
|
|
118
|
-
|
|
119
|
-
**Deferred (deliberately):**
|
|
120
|
-
|
|
121
|
-
- a generic `git(command)` tool or shell escape hatch — the entire point is
|
|
122
|
-
to not have one;
|
|
123
|
-
- write operations (commit, checkout, stash, config, worktree, hooks);
|
|
124
|
-
- network operations (fetch, push, clone, submodule) — absent by design;
|
|
125
|
-
- OS-level confinement (mount namespaces, bubblewrap) — the tools are
|
|
126
|
-
read-only by construction; an OS sandbox would strengthen the promise but
|
|
127
|
-
is platform-dependent (Linux-only in current harness sandboxes) and
|
|
128
|
-
unnecessary for the contract above;
|
|
129
|
-
- `dsh-session-scope` integration — focused scopes do not confine child
|
|
130
|
-
processes; if a scoped deployment needs subtree-fenced history, the
|
|
131
|
-
session-scope host API should grow a `visibleRoots` seam first.
|
|
132
|
-
|
|
133
|
-
## 9. Mutation test contract
|
|
134
|
-
|
|
135
|
-
The suite in `tests/mutation.test.ts` is normative for regressions:
|
|
136
|
-
|
|
137
|
-
- snapshot (work tree + `.git` minus reflogs) before/after every tool call,
|
|
138
|
-
twice in sequence, must be byte-identical;
|
|
139
|
-
- a repository whose `.git/config` wires `diff.external`, a textconv driver
|
|
140
|
-
and `core.fsmonitor` to a marker-writing helper must never produce the
|
|
141
|
-
marker from any tool call, while still returning correct output;
|
|
142
|
-
- poisoned `GIT_DIR` / `GIT_EXTERNAL_DIFF` / `GIT_INDEX_FILE` /
|
|
143
|
-
`GIT_WORK_TREE` / `GIT_OBJECT_DIRECTORY` in the parent environment must
|
|
144
|
-
not redirect reads;
|
|
145
|
-
- `../` traversal in any path argument must be rejected before spawning;
|
|
146
|
-
- an oversized patch must be truncated and flagged;
|
|
147
|
-
- missing session cwd / non-repository cwd must fail closed.
|
|
148
|
-
|
|
149
|
-
## 10. Implementation status
|
|
150
|
-
|
|
151
|
-
| Area | Status |
|
|
152
|
-
| --- | --- |
|
|
153
|
-
| `dsh_git_context` | Implemented |
|
|
154
|
-
| `dsh_git_history` | Implemented |
|
|
155
|
-
| `dsh_git_show` | Implemented |
|
|
156
|
-
| `dsh_git_blame` | Implemented |
|
|
157
|
-
| Runner hardening (argv/env/caps/kill) | Implemented |
|
|
158
|
-
| Mutation test contract (§9) | Implemented |
|
|
159
|
-
| OS-level confinement | Deferred (see §8) |
|
|
160
|
-
| session-scope subtree fencing | Deferred (see §8) |
|