@payconductor/houndsman 0.6.0 → 0.8.0
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 +20 -0
- package/package.json +1 -1
- package/src/cli/commands/command.ts +10 -1
- package/src/cli/commands/context-command.ts +40 -2
- package/src/cli/commands/hunt-command.ts +28 -2
- package/src/cli/commands/review-command.ts +30 -2
- package/src/cli/commands/server-command.ts +7 -1
- package/src/cli/commands/worktree-command.ts +118 -0
- package/src/cli/help.ts +13 -3
- package/src/cli/index.ts +14 -4
- package/src/core/ast/service.ts +7 -2
- package/src/core/brief.ts +1 -0
- package/src/core/commands/context.ts +9 -1
- package/src/core/commands/findings.ts +17 -6
- package/src/core/commands/review.ts +13 -0
- package/src/core/commands/scaffold.ts +4 -2
- package/src/core/config.ts +13 -0
- package/src/core/defaults.ts +8 -1
- package/src/core/engine/houndsman-engine.ts +26 -3
- package/src/core/engine/review-scope.ts +17 -22
- package/src/core/engine/review-session.ts +25 -2
- package/src/core/engine/review-workspace.ts +204 -0
- package/src/core/git-runner.ts +25 -13
- package/src/core/github-review.ts +10 -7
- package/src/core/jev/jev-detector.ts +10 -2
- package/src/core/orchestrator.ts +62 -13
- package/src/core/project-config.ts +12 -0
- package/src/core/services/gh-client.ts +38 -22
- package/src/core/services/git-repository.ts +46 -0
- package/src/core/services/pr-resolver.ts +147 -0
- package/src/core/services/worktree-manager.ts +269 -0
- package/src/core/storage/file/file-storage.ts +14 -1
- package/src/core/storage/paths.ts +30 -1
- package/src/core/storage/sqlite/mappers.ts +4 -0
- package/src/core/storage/sqlite/schema.ts +13 -3
- package/src/core/storage/sqlite/sqlite-storage.ts +28 -8
- package/src/core/storage/types.ts +12 -0
- package/src/core/storage/work-area.ts +20 -1
- package/src/core/types.ts +2 -0
- package/src/harness/hunt/hunt-options.ts +17 -0
- package/src/harness/hunt/hunt-session.ts +7 -20
- package/src/index.ts +31 -0
- package/src/server/client/App.tsx +86 -22
- package/src/server/client/CommentsPanel.tsx +255 -0
- package/src/server/client/DeleteRunModal.tsx +163 -0
- package/src/server/client/FindingCard.tsx +12 -4
- package/src/server/client/FindingsList.tsx +4 -1
- package/src/server/client/ResumePanel.tsx +8 -8
- package/src/server/client/Sidebar.tsx +452 -0
- package/src/server/client/SubmitModal.tsx +61 -5
- package/src/server/client/icons.tsx +29 -0
- package/src/server/client/index.tsx +1 -1
- package/src/server/client/utils/constants.ts +2 -0
- package/src/server/client/utils/format.ts +47 -2
- package/src/server/client/utils/types.ts +108 -0
- package/src/server/editor.ts +129 -0
- package/src/server/index.ts +205 -3
- package/src/server/pr-comments.ts +256 -0
- package/src/server/pr-inbox.ts +213 -0
- package/src/server/run-repository.ts +136 -3
- package/src/skill/README.md +11 -3
- package/src/skill/SKILL.md +44 -10
- package/src/skill/hunt.md +33 -7
- package/src/tests/__snapshots__/baseline.test.ts.snap +94 -24
- package/src/tests/ai-core.test.ts +2 -2
- package/src/tests/cli.test.ts +35 -1
- package/src/tests/github-review.test.ts +9 -0
- package/src/tests/server-editor.test.ts +100 -0
- package/src/tests/server.test.ts +333 -2
- package/src/tests/worktree.test.ts +369 -0
- package/src/server/client/RunList.tsx +0 -75
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Modal "Excluir execução": confirma a remoção definitiva da run.
|
|
3
|
+
*
|
|
4
|
+
* Apaga a review, findings, histórico de triagem, artefatos e o staging local.
|
|
5
|
+
* Quando a run tem worktree, oferece removê-lo junto (alterações não commitadas
|
|
6
|
+
* nele são descartadas).
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { useEffect, useState } from "preact/hooks";
|
|
10
|
+
import { Spinner, Trash } from "./icons";
|
|
11
|
+
import { api } from "./utils/api";
|
|
12
|
+
import type { DeleteRunResponse } from "./utils/types";
|
|
13
|
+
|
|
14
|
+
export function DeleteRunModal({
|
|
15
|
+
slug,
|
|
16
|
+
branch,
|
|
17
|
+
worktreePath,
|
|
18
|
+
open,
|
|
19
|
+
onClose,
|
|
20
|
+
onDeleted,
|
|
21
|
+
}: {
|
|
22
|
+
slug: string;
|
|
23
|
+
branch: string;
|
|
24
|
+
worktreePath?: string;
|
|
25
|
+
open: boolean;
|
|
26
|
+
onClose: () => void;
|
|
27
|
+
onDeleted: (result: DeleteRunResponse) => void;
|
|
28
|
+
}) {
|
|
29
|
+
const [removeWorktree, setRemoveWorktree] = useState(Boolean(worktreePath));
|
|
30
|
+
const [deleting, setDeleting] = useState(false);
|
|
31
|
+
const [error, setError] = useState("");
|
|
32
|
+
|
|
33
|
+
useEffect(() => {
|
|
34
|
+
if (!open) return;
|
|
35
|
+
setRemoveWorktree(Boolean(worktreePath));
|
|
36
|
+
setDeleting(false);
|
|
37
|
+
setError("");
|
|
38
|
+
}, [open, worktreePath]);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (!open) return;
|
|
42
|
+
const onKey = (event: KeyboardEvent) => {
|
|
43
|
+
if (event.key === "Escape" && !deleting) onClose();
|
|
44
|
+
};
|
|
45
|
+
document.addEventListener("keydown", onKey);
|
|
46
|
+
return () => document.removeEventListener("keydown", onKey);
|
|
47
|
+
}, [open, deleting, onClose]);
|
|
48
|
+
|
|
49
|
+
if (!open) return null;
|
|
50
|
+
|
|
51
|
+
const confirm = async () => {
|
|
52
|
+
setDeleting(true);
|
|
53
|
+
setError("");
|
|
54
|
+
try {
|
|
55
|
+
const suffix = removeWorktree && worktreePath ? "?worktree=1" : "";
|
|
56
|
+
const result = await api<DeleteRunResponse>(`/api/runs/${slug}${suffix}`, {
|
|
57
|
+
method: "DELETE",
|
|
58
|
+
});
|
|
59
|
+
if (!result.deleted) throw new Error("A execução não foi encontrada.");
|
|
60
|
+
onDeleted(result);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
setError(err instanceof Error ? err.message : String(err));
|
|
63
|
+
} finally {
|
|
64
|
+
setDeleting(false);
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
return (
|
|
69
|
+
<div class="modal-layer">
|
|
70
|
+
<button
|
|
71
|
+
type="button"
|
|
72
|
+
class="modal-backdrop"
|
|
73
|
+
aria-label="Fechar"
|
|
74
|
+
title="Fechar"
|
|
75
|
+
onClick={() => {
|
|
76
|
+
if (!deleting) onClose();
|
|
77
|
+
}}
|
|
78
|
+
/>
|
|
79
|
+
<div
|
|
80
|
+
class="modal"
|
|
81
|
+
role="dialog"
|
|
82
|
+
aria-modal="true"
|
|
83
|
+
aria-label="Excluir execução"
|
|
84
|
+
>
|
|
85
|
+
<header class="modal-head">
|
|
86
|
+
<div>
|
|
87
|
+
<h2 class="modal-title">Excluir execução</h2>
|
|
88
|
+
<p class="modal-sub">
|
|
89
|
+
Esta ação é permanente e apaga a revisão de{" "}
|
|
90
|
+
<code>{branch}</code>.
|
|
91
|
+
</p>
|
|
92
|
+
</div>
|
|
93
|
+
<button
|
|
94
|
+
type="button"
|
|
95
|
+
class="modal-close"
|
|
96
|
+
title="Fechar"
|
|
97
|
+
disabled={deleting}
|
|
98
|
+
onClick={onClose}
|
|
99
|
+
>
|
|
100
|
+
×
|
|
101
|
+
</button>
|
|
102
|
+
</header>
|
|
103
|
+
|
|
104
|
+
<div class="modal-body">
|
|
105
|
+
<p class="modal-warn">
|
|
106
|
+
Serão removidos os findings, o histórico de triagem, todos os
|
|
107
|
+
artefatos (briefs, classificação, manifesto, relatório, verify) e
|
|
108
|
+
o staging local desta run. O envio já feito ao GitHub não é
|
|
109
|
+
desfeito.
|
|
110
|
+
</p>
|
|
111
|
+
|
|
112
|
+
{worktreePath && (
|
|
113
|
+
<label class="modal-check">
|
|
114
|
+
<input
|
|
115
|
+
type="checkbox"
|
|
116
|
+
checked={removeWorktree}
|
|
117
|
+
disabled={deleting}
|
|
118
|
+
onChange={(event) =>
|
|
119
|
+
setRemoveWorktree(event.currentTarget.checked)
|
|
120
|
+
}
|
|
121
|
+
/>
|
|
122
|
+
<span>
|
|
123
|
+
Excluir também o worktree desta execução. Alterações não
|
|
124
|
+
commitadas nele serão perdidas.
|
|
125
|
+
<br />
|
|
126
|
+
<code>{worktreePath}</code>
|
|
127
|
+
</span>
|
|
128
|
+
</label>
|
|
129
|
+
)}
|
|
130
|
+
|
|
131
|
+
{error && <p class="error">{error}</p>}
|
|
132
|
+
</div>
|
|
133
|
+
|
|
134
|
+
<footer class="modal-foot">
|
|
135
|
+
<span class="submit-label">Run: {branch}</span>
|
|
136
|
+
<div class="submit-actions">
|
|
137
|
+
<button
|
|
138
|
+
type="button"
|
|
139
|
+
class="btn"
|
|
140
|
+
disabled={deleting}
|
|
141
|
+
onClick={onClose}
|
|
142
|
+
>
|
|
143
|
+
Cancelar
|
|
144
|
+
</button>
|
|
145
|
+
<button
|
|
146
|
+
type="button"
|
|
147
|
+
class="btn danger"
|
|
148
|
+
disabled={deleting}
|
|
149
|
+
onClick={() => void confirm()}
|
|
150
|
+
>
|
|
151
|
+
{deleting ? (
|
|
152
|
+
<Spinner size={15} class="icon spin" />
|
|
153
|
+
) : (
|
|
154
|
+
<Trash size={15} />
|
|
155
|
+
)}
|
|
156
|
+
{deleting ? "Excluindo…" : "Excluir execução"}
|
|
157
|
+
</button>
|
|
158
|
+
</div>
|
|
159
|
+
</footer>
|
|
160
|
+
</div>
|
|
161
|
+
</div>
|
|
162
|
+
);
|
|
163
|
+
}
|
|
@@ -21,8 +21,8 @@ import { MarkdownText } from "./MarkdownText";
|
|
|
21
21
|
import { api } from "./utils/api";
|
|
22
22
|
import { highlightCode, languageFromFile } from "./utils/code-highlight";
|
|
23
23
|
import { DECISION_LABELS, DECISIONS, SEVERITY_LABELS } from "./utils/constants";
|
|
24
|
-
import {
|
|
25
|
-
import type { Finding } from "./utils/types";
|
|
24
|
+
import { editorFileLink, editorLabel } from "./utils/format";
|
|
25
|
+
import type { EditorContext, Finding } from "./utils/types";
|
|
26
26
|
|
|
27
27
|
/** Debounce do auto-save do comentário (ms). */
|
|
28
28
|
const AUTOSAVE_MS = 700;
|
|
@@ -32,10 +32,12 @@ const SAVED_VISIBLE_MS = 1800;
|
|
|
32
32
|
export function FindingCard({
|
|
33
33
|
slug,
|
|
34
34
|
finding,
|
|
35
|
+
editor,
|
|
35
36
|
onUpdated,
|
|
36
37
|
}: {
|
|
37
38
|
slug: string;
|
|
38
39
|
finding: Finding;
|
|
40
|
+
editor: EditorContext;
|
|
39
41
|
onUpdated: (finding: Finding) => void;
|
|
40
42
|
}) {
|
|
41
43
|
const isDone = finding.execState === "done";
|
|
@@ -104,7 +106,9 @@ export function FindingCard({
|
|
|
104
106
|
}, [comment, patch]);
|
|
105
107
|
|
|
106
108
|
const location = finding.line ? `${finding.file}:${finding.line}` : finding.file;
|
|
107
|
-
const href = finding.absFile
|
|
109
|
+
const href = finding.absFile
|
|
110
|
+
? editorFileLink(finding.absFile, finding.line, editor)
|
|
111
|
+
: null;
|
|
108
112
|
const highlighted = useMemo(
|
|
109
113
|
() =>
|
|
110
114
|
finding.evidence
|
|
@@ -132,7 +136,11 @@ export function FindingCard({
|
|
|
132
136
|
</button>
|
|
133
137
|
<span class="sev-badge">{SEVERITY_LABELS[finding.severity]}</span>
|
|
134
138
|
{href ? (
|
|
135
|
-
<a
|
|
139
|
+
<a
|
|
140
|
+
class="loc"
|
|
141
|
+
href={href}
|
|
142
|
+
title={`Abrir ${location} no ${editorLabel(editor)}`}
|
|
143
|
+
>
|
|
136
144
|
<CodeIcon size={13} />
|
|
137
145
|
{location}
|
|
138
146
|
<ExternalLink size={12} class="ext" />
|
|
@@ -6,15 +6,17 @@ import { useMemo } from "preact/hooks";
|
|
|
6
6
|
import { FindingCard } from "./FindingCard";
|
|
7
7
|
import { Alert } from "./icons";
|
|
8
8
|
import { SEVERITY_LABELS, SEVERITY_ORDER } from "./utils/constants";
|
|
9
|
-
import type { Finding, Severity } from "./utils/types";
|
|
9
|
+
import type { EditorContext, Finding, Severity } from "./utils/types";
|
|
10
10
|
|
|
11
11
|
export function FindingsList({
|
|
12
12
|
slug,
|
|
13
13
|
findings,
|
|
14
|
+
editor,
|
|
14
15
|
onUpdated,
|
|
15
16
|
}: {
|
|
16
17
|
slug: string;
|
|
17
18
|
findings: Finding[];
|
|
19
|
+
editor: EditorContext;
|
|
18
20
|
onUpdated: (finding: Finding) => void;
|
|
19
21
|
}) {
|
|
20
22
|
const grouped = useMemo(() => {
|
|
@@ -45,6 +47,7 @@ export function FindingsList({
|
|
|
45
47
|
key={finding.id}
|
|
46
48
|
slug={slug}
|
|
47
49
|
finding={finding}
|
|
50
|
+
editor={editor}
|
|
48
51
|
onUpdated={onUpdated}
|
|
49
52
|
/>
|
|
50
53
|
))}
|
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
|
|
11
11
|
import type { ComponentChildren } from "preact";
|
|
12
12
|
import { useEffect, useMemo, useState } from "preact/hooks";
|
|
13
|
-
import { ExternalLink, Layers } from "./icons";
|
|
13
|
+
import { ExternalLink, Layers, Trash } from "./icons";
|
|
14
14
|
import { MarkdownBlocks } from "./MarkdownText";
|
|
15
15
|
import { api } from "./utils/api";
|
|
16
16
|
import { SEVERITY_LABELS, SEVERITY_ORDER } from "./utils/constants";
|
|
@@ -79,13 +79,13 @@ export function ResumePanel({
|
|
|
79
79
|
branch,
|
|
80
80
|
findings,
|
|
81
81
|
summary,
|
|
82
|
-
|
|
82
|
+
onRequestDelete,
|
|
83
83
|
}: {
|
|
84
84
|
slug: string;
|
|
85
85
|
branch: string;
|
|
86
86
|
findings: Finding[];
|
|
87
87
|
summary: Summary;
|
|
88
|
-
|
|
88
|
+
onRequestDelete: () => void;
|
|
89
89
|
}) {
|
|
90
90
|
const [resume, setResume] = useState("");
|
|
91
91
|
const [detail, setDetail] = useState<RunDetail | null>(null);
|
|
@@ -241,12 +241,12 @@ export function ResumePanel({
|
|
|
241
241
|
<span class={`resume-status ${status.tone}`}>{status.label}</span>
|
|
242
242
|
<button
|
|
243
243
|
type="button"
|
|
244
|
-
class="btn"
|
|
245
|
-
title="
|
|
246
|
-
onClick={
|
|
244
|
+
class="btn danger"
|
|
245
|
+
title="Excluir esta execução, seus findings e artefatos locais"
|
|
246
|
+
onClick={onRequestDelete}
|
|
247
247
|
>
|
|
248
|
-
<
|
|
249
|
-
|
|
248
|
+
<Trash size={14} />
|
|
249
|
+
Excluir
|
|
250
250
|
</button>
|
|
251
251
|
</div>
|
|
252
252
|
</section>
|