@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.
Files changed (71) hide show
  1. package/README.md +20 -0
  2. package/package.json +1 -1
  3. package/src/cli/commands/command.ts +10 -1
  4. package/src/cli/commands/context-command.ts +40 -2
  5. package/src/cli/commands/hunt-command.ts +28 -2
  6. package/src/cli/commands/review-command.ts +30 -2
  7. package/src/cli/commands/server-command.ts +7 -1
  8. package/src/cli/commands/worktree-command.ts +118 -0
  9. package/src/cli/help.ts +13 -3
  10. package/src/cli/index.ts +14 -4
  11. package/src/core/ast/service.ts +7 -2
  12. package/src/core/brief.ts +1 -0
  13. package/src/core/commands/context.ts +9 -1
  14. package/src/core/commands/findings.ts +17 -6
  15. package/src/core/commands/review.ts +13 -0
  16. package/src/core/commands/scaffold.ts +4 -2
  17. package/src/core/config.ts +13 -0
  18. package/src/core/defaults.ts +8 -1
  19. package/src/core/engine/houndsman-engine.ts +26 -3
  20. package/src/core/engine/review-scope.ts +17 -22
  21. package/src/core/engine/review-session.ts +25 -2
  22. package/src/core/engine/review-workspace.ts +204 -0
  23. package/src/core/git-runner.ts +25 -13
  24. package/src/core/github-review.ts +10 -7
  25. package/src/core/jev/jev-detector.ts +10 -2
  26. package/src/core/orchestrator.ts +62 -13
  27. package/src/core/project-config.ts +12 -0
  28. package/src/core/services/gh-client.ts +38 -22
  29. package/src/core/services/git-repository.ts +46 -0
  30. package/src/core/services/pr-resolver.ts +147 -0
  31. package/src/core/services/worktree-manager.ts +269 -0
  32. package/src/core/storage/file/file-storage.ts +14 -1
  33. package/src/core/storage/paths.ts +30 -1
  34. package/src/core/storage/sqlite/mappers.ts +4 -0
  35. package/src/core/storage/sqlite/schema.ts +13 -3
  36. package/src/core/storage/sqlite/sqlite-storage.ts +28 -8
  37. package/src/core/storage/types.ts +12 -0
  38. package/src/core/storage/work-area.ts +20 -1
  39. package/src/core/types.ts +2 -0
  40. package/src/harness/hunt/hunt-options.ts +17 -0
  41. package/src/harness/hunt/hunt-session.ts +7 -20
  42. package/src/index.ts +31 -0
  43. package/src/server/client/App.tsx +86 -22
  44. package/src/server/client/CommentsPanel.tsx +255 -0
  45. package/src/server/client/DeleteRunModal.tsx +163 -0
  46. package/src/server/client/FindingCard.tsx +12 -4
  47. package/src/server/client/FindingsList.tsx +4 -1
  48. package/src/server/client/ResumePanel.tsx +8 -8
  49. package/src/server/client/Sidebar.tsx +452 -0
  50. package/src/server/client/SubmitModal.tsx +61 -5
  51. package/src/server/client/icons.tsx +29 -0
  52. package/src/server/client/index.tsx +1 -1
  53. package/src/server/client/utils/constants.ts +2 -0
  54. package/src/server/client/utils/format.ts +47 -2
  55. package/src/server/client/utils/types.ts +108 -0
  56. package/src/server/editor.ts +129 -0
  57. package/src/server/index.ts +205 -3
  58. package/src/server/pr-comments.ts +256 -0
  59. package/src/server/pr-inbox.ts +213 -0
  60. package/src/server/run-repository.ts +136 -3
  61. package/src/skill/README.md +11 -3
  62. package/src/skill/SKILL.md +44 -10
  63. package/src/skill/hunt.md +33 -7
  64. package/src/tests/__snapshots__/baseline.test.ts.snap +94 -24
  65. package/src/tests/ai-core.test.ts +2 -2
  66. package/src/tests/cli.test.ts +35 -1
  67. package/src/tests/github-review.test.ts +9 -0
  68. package/src/tests/server-editor.test.ts +100 -0
  69. package/src/tests/server.test.ts +333 -2
  70. package/src/tests/worktree.test.ts +369 -0
  71. 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 { vscodeFileLink } from "./utils/format";
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 ? vscodeFileLink(finding.absFile, finding.line) : null;
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 class="loc" href={href} title={`Abrir ${location} no VS Code`}>
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
- onOpenArtifacts,
82
+ onRequestDelete,
83
83
  }: {
84
84
  slug: string;
85
85
  branch: string;
86
86
  findings: Finding[];
87
87
  summary: Summary;
88
- onOpenArtifacts: () => void;
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="Ver os artefatos brutos da revisão"
246
- onClick={onOpenArtifacts}
244
+ class="btn danger"
245
+ title="Excluir esta execução, seus findings e artefatos locais"
246
+ onClick={onRequestDelete}
247
247
  >
248
- <Layers size={14} />
249
- Ver artefatos
248
+ <Trash size={14} />
249
+ Excluir
250
250
  </button>
251
251
  </div>
252
252
  </section>