Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "dsh-cctui",
"version": "0.3.9",
"version": "0.3.10",
"description": "Claude-Code-style terminal UI for deepseek-harness, ported from clawcodex ui-tui",
"license": "MIT",
"repository": {
Expand Down
15 changes: 11 additions & 4 deletions scripts/tool-gallery.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,13 @@
"glob": 'PROBE glob {"pattern": "src/components/*.tsx"}',
"bash": 'PROBE bash {"command": "ls -1 src | head -12", "description": "list src"}',
"bash_fail": 'PROBE bash {"command": "ls /no/such/dir", "description": "failing ls"}',
"write": 'PROBE write {"file_path": "e2e-scratch/gallery.txt", "content": "alpha\\nbeta\\ngamma\\n"}',
"write": 'PROBE write {"file_path": "e2e-scratch/gallery-write.txt", "content": "alpha\\nbeta\\ngamma\\n"}',
# the harness refuses to edit a file this session has not observed, so the
# scenario writes it, reads it, then edits it
"edit": [
'PROBE write {"file_path": "e2e-scratch/gallery.txt", "content": "alpha\\nbeta\\ngamma\\n"}',
'PROBE edit {"file_path": "e2e-scratch/gallery.txt", "old_string": "beta", "new_string": "BETA-changed"}',
'PROBE write {"file_path": "e2e-scratch/gallery-edit.txt", "content": "alpha\\nbeta\\ngamma\\n"}',
'PROBE read {"file_path": "e2e-scratch/gallery-edit.txt"}',
'PROBE edit {"file_path": "e2e-scratch/gallery-edit.txt", "old_string": "beta", "new_string": "BETA-changed"}',
],
# reaches the network on purpose — the fetch card is a retrieval summary
"web_fetch": 'PROBE web_fetch {"url": "https://example.com"}',
Expand All @@ -83,7 +86,8 @@
),
"skill": 'PROBE skill {"name": "nonexistent-skill"}',
"read_image": 'PROBE read_image {"file_path": "e2e-scratch/gallery.png"}',
"str_replace_editor": 'PROBE str_replace_editor {"command": "view", "path": "src/domain/usage.ts"}',
# this one insists on an absolute path
"str_replace_editor": ('PROBE str_replace_editor {"command": "view", "path": "%s/src/domain/usage.ts"}' % ROOT),
"job_output": 'PROBE job_output {"job_id": "no-such-job"}',
"send_message": 'PROBE send_message {"subagent_id": "no-such-agent", "message": "hello"}',
"get_goal": 'PROBE get_goal {}',
Expand Down Expand Up @@ -111,6 +115,9 @@


def bootstrap() -> None:
# the mutation scenarios must CREATE their file, not find yesterday's
for leftover in (ROOT / "e2e-scratch").glob("gallery-*.txt"):
leftover.unlink()
PROFILE.mkdir(parents=True, exist_ok=True)
(PROFILE / "package.json").write_text(
json.dumps(
Expand Down
15 changes: 14 additions & 1 deletion src/__tests__/harnessClient.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,10 @@ const TOOL_CARDS: Record<string, unknown> = {
path: 'src/y.ts',
totalLines: 40
},
write: { card: 'diff', diffs: [{ newText: 'line one\nline two\n', oldText: null, path: 'notes.txt' }] }
write: { card: 'diff', diffs: [{ newText: 'line one\nline two\n', oldText: null, path: 'notes.txt' }] },
// an APPLIED HUNK rather than a whole file: it stops mid-file, so neither
// side carries a trailing newline
write_hunk: { card: 'diff', diffs: [{ newText: 'beta\nBETA', oldText: 'beta\nbeta', path: 'notes.txt' }] }
}

function makeWorld() {
Expand Down Expand Up @@ -1024,6 +1027,16 @@ describe('HarnessGatewayClient', () => {
expect(usage.context_percent).toBe(5)
})

it('drops the no-newline marker a hunk-shaped diff always produces', async () => {
const done = await runTool('write_hunk', '{"file_path":"notes.txt"}')
const lines = done.payload.structured_diff?.hunks.flatMap(hunk => hunk.lines) ?? []

// the row renderer has no marker for `\ No newline at end of file`, so it
// came out as a phantom context line numbered one past the end
expect(lines.length).toBeGreaterThan(0)
expect(lines.some(line => line.startsWith('\\'))).toBe(false)
})

it('converts diff presentation views into structured_diff payloads', async () => {
const w = makeWorld()

Expand Down
8 changes: 7 additions & 1 deletion src/harness/client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1092,7 +1092,13 @@ export class HarnessGatewayClient extends GatewayClient {
const structuredDiff: StructuredDiffPayload = {
filePath: first.path,
hunks: patch.hunks.map(h => ({
lines: h.lines,
// `\ No newline at end of file` is an artifact of diffing the
// tool's applied HUNK rather than the whole file: a fragment that
// stops mid-file has no trailing newline by construction, and the
// file it came from is usually fine. The row renderer has no marker
// for it either, so it came out as a phantom context line numbered
// one past the end. Upstream shows no such row.
lines: h.lines.filter(line => !line.startsWith('\\')),
newLines: h.newLines,
newStart: h.newStart,
oldLines: h.oldLines,
Expand Down
Loading