@crouton-kit/humanloop 0.1.0 → 0.1.3
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/dist/cli.js +82 -89
- package/dist/index.d.ts +5 -0
- package/dist/index.js +2 -0
- package/dist/tui/app.d.ts +7 -2
- package/dist/tui/app.js +303 -105
- package/dist/tui/input.d.ts +2 -1
- package/dist/tui/input.js +129 -115
- package/dist/tui/render.d.ts +10 -5
- package/dist/tui/render.js +329 -157
- package/dist/tui/terminal.js +5 -0
- package/dist/tui/tmux.d.ts +6 -2
- package/dist/tui/tmux.js +20 -2
- package/dist/types.d.ts +57 -45
- package/dist/types.js +1 -1
- package/dist/visuals/generate.d.ts +9 -4
- package/dist/visuals/generate.js +30 -39
- package/package.json +14 -2
package/dist/types.d.ts
CHANGED
|
@@ -1,50 +1,36 @@
|
|
|
1
|
-
|
|
2
|
-
export
|
|
1
|
+
import type { Key } from './tui/terminal.js';
|
|
2
|
+
export type InteractionKind = 'notify' | 'validation' | 'decision' | 'context' | 'error';
|
|
3
|
+
export interface InteractionOption {
|
|
3
4
|
id: string;
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
5
|
+
label: string;
|
|
6
|
+
description?: string;
|
|
7
|
+
shortcut?: string;
|
|
7
8
|
}
|
|
8
|
-
export interface
|
|
9
|
+
export interface Interaction {
|
|
9
10
|
id: string;
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
11
|
+
title: string;
|
|
12
|
+
subtitle?: string;
|
|
13
|
+
body?: string;
|
|
14
|
+
bodyPath?: string;
|
|
15
|
+
options: InteractionOption[];
|
|
16
|
+
allowFreetext?: boolean;
|
|
17
|
+
freetextLabel?: string;
|
|
18
|
+
kind?: InteractionKind;
|
|
14
19
|
}
|
|
15
|
-
export interface
|
|
20
|
+
export interface InteractionResponse {
|
|
16
21
|
id: string;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
rationale: string;
|
|
22
|
+
selectedOptionId?: string;
|
|
23
|
+
freetext?: string;
|
|
20
24
|
}
|
|
21
|
-
export
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
}
|
|
26
|
-
export interface ValidationAnswer {
|
|
27
|
-
id: string;
|
|
28
|
-
type: 'validation';
|
|
29
|
-
approved: boolean;
|
|
30
|
-
comment?: string;
|
|
31
|
-
}
|
|
32
|
-
export interface ChoiceAnswer {
|
|
33
|
-
id: string;
|
|
34
|
-
type: 'choice';
|
|
35
|
-
selected: string;
|
|
36
|
-
isCustom: boolean;
|
|
37
|
-
comment?: string;
|
|
38
|
-
}
|
|
39
|
-
export interface FreetextAnswer {
|
|
40
|
-
id: string;
|
|
41
|
-
type: 'freetext';
|
|
42
|
-
response: string;
|
|
25
|
+
export interface DeckSource {
|
|
26
|
+
sessionName?: string;
|
|
27
|
+
askedBy?: string;
|
|
28
|
+
blockedSince?: string;
|
|
43
29
|
}
|
|
44
|
-
export
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
30
|
+
export interface Deck {
|
|
31
|
+
title?: string;
|
|
32
|
+
source?: DeckSource;
|
|
33
|
+
interactions: Interaction[];
|
|
48
34
|
}
|
|
49
35
|
export interface VisualBlock {
|
|
50
36
|
questionId: string;
|
|
@@ -55,18 +41,16 @@ export type Phase = 'overview' | 'item-review' | 'final';
|
|
|
55
41
|
export type InputMode = null | {
|
|
56
42
|
kind: 'comment';
|
|
57
43
|
buffer: string;
|
|
44
|
+
selectedOptionId?: string;
|
|
58
45
|
} | {
|
|
59
46
|
kind: 'freetext';
|
|
60
47
|
buffer: string;
|
|
61
|
-
} | {
|
|
62
|
-
kind: 'custom-option';
|
|
63
|
-
buffer: string;
|
|
64
48
|
};
|
|
65
49
|
export interface TuiState {
|
|
66
50
|
phase: Phase;
|
|
67
51
|
currentIndex: number;
|
|
68
|
-
|
|
69
|
-
|
|
52
|
+
interactions: Interaction[];
|
|
53
|
+
responses: Map<string, InteractionResponse>;
|
|
70
54
|
visuals: Map<string, VisualBlock>;
|
|
71
55
|
inputMode: InputMode;
|
|
72
56
|
selectedAction: number;
|
|
@@ -74,3 +58,31 @@ export interface TuiState {
|
|
|
74
58
|
scrollOffset: number;
|
|
75
59
|
persist?: () => void;
|
|
76
60
|
}
|
|
61
|
+
export type GenerateVisual = (interaction: Interaction) => Promise<{
|
|
62
|
+
ok: true;
|
|
63
|
+
ansi: string;
|
|
64
|
+
markdown: string;
|
|
65
|
+
} | {
|
|
66
|
+
ok: false;
|
|
67
|
+
error: string;
|
|
68
|
+
}>;
|
|
69
|
+
export interface MountedPanelOpts {
|
|
70
|
+
deck: Deck;
|
|
71
|
+
progressPath?: string;
|
|
72
|
+
generateVisual?: GenerateVisual;
|
|
73
|
+
cols: number;
|
|
74
|
+
rows: number;
|
|
75
|
+
onProgress?: (responses: InteractionResponse[]) => void;
|
|
76
|
+
onComplete?: (responses: InteractionResponse[]) => void;
|
|
77
|
+
onExit?: () => void;
|
|
78
|
+
}
|
|
79
|
+
export interface MountedPanel {
|
|
80
|
+
handleKey(input: string, key: Key): void;
|
|
81
|
+
render(): string[];
|
|
82
|
+
handleResize(cols: number, rows: number): string[];
|
|
83
|
+
unmount(): void;
|
|
84
|
+
loadDeck(deck: Deck, opts?: {
|
|
85
|
+
progressPath?: string;
|
|
86
|
+
}): void;
|
|
87
|
+
canAcceptHostKeys(): boolean;
|
|
88
|
+
}
|
package/dist/types.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
// ──
|
|
1
|
+
// ── v2 shapes (v1 schema dropped per cycle-16 user pivot — humanloop is v2-only) ──
|
|
2
2
|
export {};
|
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
1
|
+
import type { Interaction } from '../types.js';
|
|
2
|
+
export declare function defaultGenerateVisual(interaction: Interaction, conversationContext: string): Promise<{
|
|
3
|
+
ok: true;
|
|
4
|
+
ansi: string;
|
|
5
|
+
markdown: string;
|
|
6
|
+
} | {
|
|
7
|
+
ok: false;
|
|
8
|
+
error: string;
|
|
9
|
+
}>;
|
package/dist/visuals/generate.js
CHANGED
|
@@ -14,12 +14,12 @@ If one sentence captures the current state, write one sentence. If they need to
|
|
|
14
14
|
|
|
15
15
|
# Directives (termrender-flavored markdown)
|
|
16
16
|
|
|
17
|
-
:::panel{title="T" color="c"}
|
|
18
|
-
:::tree{color="c"}
|
|
19
|
-
:::
|
|
20
|
-
|
|
17
|
+
:::panel{title="T" color="c"} Bordered box (colors: red|green|yellow|blue|magenta|cyan|white|gray)
|
|
18
|
+
:::tree{color="c"} Indented hierarchy (2-space indent = nesting)
|
|
19
|
+
:::callout{type="info|warning|error|success"} Status callout with icon
|
|
20
|
+
::::columns / :::col{width="50%"} Side-by-side layout (use 4 colons on the outer columns)
|
|
21
21
|
|
|
22
|
-
Each opens with ::: and closes with :::. Standard markdown also works: **bold**, *italic*, \`code\`, bullets.
|
|
22
|
+
Each opens with ::: and closes with :::. GFM tables (\`| col | col |\` with a \`| --- |\` separator) render directly — no directive needed. Standard markdown also works: **bold**, *italic*, \`code\`, bullets.
|
|
23
23
|
|
|
24
24
|
# Critical: ASCII art must live inside a :::panel
|
|
25
25
|
|
|
@@ -33,7 +33,7 @@ If the conversation doesn't contain enough context to write a grounded briefing,
|
|
|
33
33
|
|
|
34
34
|
# Hard rules
|
|
35
35
|
|
|
36
|
-
-
|
|
36
|
+
- When nesting directives, the outer fence needs strictly more colons than the inner — e.g. \`::::columns\` wrapping \`:::col\`. Don't nest a panel inside a panel.
|
|
37
37
|
- Never wrap output in backtick fences
|
|
38
38
|
- Never repeat the question/statement text
|
|
39
39
|
- Never write "tradeoffs to consider" or "here are some options"
|
|
@@ -93,37 +93,28 @@ function tryTermrender(markdown, width) {
|
|
|
93
93
|
return null;
|
|
94
94
|
}
|
|
95
95
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
else {
|
|
121
|
-
onUpdate(question.id, {
|
|
122
|
-
questionId: question.id,
|
|
123
|
-
content: '',
|
|
124
|
-
status: 'error',
|
|
125
|
-
});
|
|
126
|
-
}
|
|
127
|
-
});
|
|
128
|
-
await Promise.all(tasks);
|
|
96
|
+
// defaultGenerateVisual matches the GenerateVisual contract for use with
|
|
97
|
+
// mountPanel. Width is read from process.stdout.columns so callers that
|
|
98
|
+
// embed humanloop in a sub-region should supply their own closure that bakes
|
|
99
|
+
// in the correct width.
|
|
100
|
+
export async function defaultGenerateVisual(interaction, conversationContext) {
|
|
101
|
+
const width = Math.max(40, Math.min((process.stdout.columns || 80) - 4, 76));
|
|
102
|
+
const optionsSummary = interaction.options.length > 0
|
|
103
|
+
? `\nOptions: ${interaction.options.map((o) => o.label).join(' | ')}`
|
|
104
|
+
: '';
|
|
105
|
+
const subtitleLine = interaction.subtitle ? `\nContext: ${interaction.subtitle}` : '';
|
|
106
|
+
const questionText = `Title: "${interaction.title}"${subtitleLine}${optionsSummary}`;
|
|
107
|
+
const prompt = conversationContext
|
|
108
|
+
? `Here is the conversation so far:\n\n${conversationContext}\n\n---\n\nGenerate a visual context block for this decision point:\n\n${questionText}`
|
|
109
|
+
: `Generate a visual context block for this decision point:\n\n${questionText}`;
|
|
110
|
+
const result = await callHaiku(prompt, VISUAL_SYSTEM_PROMPT);
|
|
111
|
+
if (result) {
|
|
112
|
+
const markdown = result
|
|
113
|
+
.replace(/^```[\w]*\n?/gm, '')
|
|
114
|
+
.replace(/^```\s*$/gm, '')
|
|
115
|
+
.trim();
|
|
116
|
+
const ansi = renderWithTermrender(markdown, width);
|
|
117
|
+
return { ok: true, ansi, markdown };
|
|
118
|
+
}
|
|
119
|
+
return { ok: false, error: 'haiku returned no output' };
|
|
129
120
|
}
|
package/package.json
CHANGED
|
@@ -1,11 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@crouton-kit/humanloop",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "Human-in-the-loop decision TUI — agents write questions, humans answer them",
|
|
5
5
|
"type": "module",
|
|
6
|
+
"main": "dist/index.js",
|
|
7
|
+
"types": "dist/index.d.ts",
|
|
6
8
|
"bin": {
|
|
7
9
|
"hl": "dist/cli.js"
|
|
8
10
|
},
|
|
11
|
+
"exports": {
|
|
12
|
+
".": {
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"types": "./dist/index.d.ts"
|
|
15
|
+
},
|
|
16
|
+
"./cli": {
|
|
17
|
+
"import": "./dist/cli.js"
|
|
18
|
+
}
|
|
19
|
+
},
|
|
9
20
|
"files": [
|
|
10
21
|
"dist",
|
|
11
22
|
"commands"
|
|
@@ -13,7 +24,8 @@
|
|
|
13
24
|
"scripts": {
|
|
14
25
|
"build": "tsc",
|
|
15
26
|
"dev": "tsx src/cli.ts",
|
|
16
|
-
"link": "npm link"
|
|
27
|
+
"link": "npm link",
|
|
28
|
+
"test": "tsx src/__tests__/mount-panel.test.ts"
|
|
17
29
|
},
|
|
18
30
|
"dependencies": {
|
|
19
31
|
"@r-cli/sdk": "^1.3.0",
|