@jxtools/promptline 1.0.0 → 1.3.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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jxtools/promptline",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.3.0",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"license": "ISC",
|
|
6
6
|
"bin": {
|
|
@@ -18,6 +18,10 @@
|
|
|
18
18
|
"index.html",
|
|
19
19
|
"README.md"
|
|
20
20
|
],
|
|
21
|
+
"repository": {
|
|
22
|
+
"type": "git",
|
|
23
|
+
"url": "https://github.com/juancruzrossi/promptline"
|
|
24
|
+
},
|
|
21
25
|
"publishConfig": {
|
|
22
26
|
"access": "public"
|
|
23
27
|
},
|
|
@@ -30,18 +30,21 @@ if [ -z "$CWD" ]; then
|
|
|
30
30
|
exit 0
|
|
31
31
|
fi
|
|
32
32
|
|
|
33
|
-
# ---
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
33
|
+
# --- Search for existing session across all projects ---
|
|
34
|
+
QUEUES_BASE="$HOME/.promptline/queues"
|
|
35
|
+
EXISTING=$(find "$QUEUES_BASE" -maxdepth 2 -name "$SESSION_ID.json" -print -quit 2>/dev/null || true)
|
|
36
|
+
|
|
37
|
+
if [ -n "$EXISTING" ]; then
|
|
38
|
+
QUEUE_FILE="$EXISTING"
|
|
39
|
+
PROJECT=$(basename "$(dirname "$EXISTING")")
|
|
40
|
+
QUEUE_DIR="$(dirname "$EXISTING")"
|
|
41
|
+
else
|
|
42
|
+
# No session file -> nothing to do
|
|
42
43
|
exit 0
|
|
43
44
|
fi
|
|
44
45
|
|
|
46
|
+
export QUEUE_FILE SESSION_ID CWD PROJECT TRANSCRIPT_PATH STOP_HOOK_ACTIVE
|
|
47
|
+
|
|
45
48
|
# --- Process queue with python3 ---
|
|
46
49
|
RESULT=$(python3 << 'PYEOF'
|
|
47
50
|
import json
|
|
@@ -21,8 +21,17 @@ if [ -z "$CWD" ] || [ -z "$SESSION_ID" ]; then
|
|
|
21
21
|
exit 0
|
|
22
22
|
fi
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
|
|
24
|
+
# Search for existing session across all projects
|
|
25
|
+
QUEUES_BASE="$HOME/.promptline/queues"
|
|
26
|
+
EXISTING=$(find "$QUEUES_BASE" -maxdepth 2 -name "$SESSION_ID.json" -print -quit 2>/dev/null || true)
|
|
27
|
+
|
|
28
|
+
if [ -n "$EXISTING" ]; then
|
|
29
|
+
QUEUE_FILE="$EXISTING"
|
|
30
|
+
else
|
|
31
|
+
# No session to close
|
|
32
|
+
exit 0
|
|
33
|
+
fi
|
|
34
|
+
|
|
26
35
|
export QUEUE_FILE
|
|
27
36
|
|
|
28
37
|
python3 << 'PYEOF'
|
|
@@ -24,11 +24,20 @@ if [ -z "$CWD" ] || [ -z "$SESSION_ID" ]; then
|
|
|
24
24
|
exit 0
|
|
25
25
|
fi
|
|
26
26
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
27
|
+
# Search for existing session across all projects
|
|
28
|
+
QUEUES_BASE="$HOME/.promptline/queues"
|
|
29
|
+
EXISTING=$(find "$QUEUES_BASE" -maxdepth 2 -name "$SESSION_ID.json" -print -quit 2>/dev/null || true)
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
if [ -n "$EXISTING" ]; then
|
|
32
|
+
QUEUE_FILE="$EXISTING"
|
|
33
|
+
PROJECT=$(basename "$(dirname "$EXISTING")")
|
|
34
|
+
QUEUE_DIR="$(dirname "$EXISTING")"
|
|
35
|
+
else
|
|
36
|
+
PROJECT=$(basename "$CWD")
|
|
37
|
+
QUEUE_DIR="$QUEUES_BASE/$PROJECT"
|
|
38
|
+
QUEUE_FILE="$QUEUE_DIR/$SESSION_ID.json"
|
|
39
|
+
mkdir -p "$QUEUE_DIR"
|
|
40
|
+
fi
|
|
32
41
|
|
|
33
42
|
export QUEUE_FILE SESSION_ID CWD PROJECT TRANSCRIPT_PATH
|
|
34
43
|
|
|
@@ -3,7 +3,7 @@ import { join } from 'node:path';
|
|
|
3
3
|
import type { SessionQueue, Prompt, PromptStatus, SessionStatus, QueueStatus, ProjectView, SessionWithStatus } from '../types/queue.ts';
|
|
4
4
|
|
|
5
5
|
export const SESSION_ACTIVE_TIMEOUT_MS = 60_000;
|
|
6
|
-
export const
|
|
6
|
+
export const SESSION_ABANDONED_TIMEOUT_MS = 24 * 60 * 60_000; // 24h safety net
|
|
7
7
|
|
|
8
8
|
export function ensureProjectDir(queuesDir: string, project: string): void {
|
|
9
9
|
mkdirSync(join(queuesDir, project), { recursive: true });
|
|
@@ -52,7 +52,9 @@ export function withComputedStatus(session: SessionQueue): SessionQueue & { stat
|
|
|
52
52
|
export function isSessionVisible(session: SessionQueue, now: number = Date.now()): boolean {
|
|
53
53
|
if (hasPendingWork(session)) return true;
|
|
54
54
|
if (session.closedAt != null) return false;
|
|
55
|
-
|
|
55
|
+
if (!session.sessionName) return false;
|
|
56
|
+
const msSinceStart = now - new Date(session.startedAt).getTime();
|
|
57
|
+
return msSinceStart <= SESSION_ABANDONED_TIMEOUT_MS;
|
|
56
58
|
}
|
|
57
59
|
|
|
58
60
|
export function loadProjectView(queuesDir: string, project: string): ProjectView | null {
|
|
@@ -206,19 +206,22 @@ export function PromptCard({
|
|
|
206
206
|
{styles.label}
|
|
207
207
|
</span>
|
|
208
208
|
|
|
209
|
-
{/*
|
|
209
|
+
{/* Delete button */}
|
|
210
210
|
{!editing && !isRunning && (
|
|
211
211
|
<button
|
|
212
212
|
type="button"
|
|
213
213
|
onClick={handleDelete}
|
|
214
214
|
className={[
|
|
215
|
-
'
|
|
216
|
-
'hover:text-red-400 hover:bg-red-400/10',
|
|
215
|
+
'p-1 rounded cursor-pointer',
|
|
216
|
+
'text-[var(--color-muted)]/40 hover:text-red-400 hover:bg-red-400/10',
|
|
217
217
|
'transition-all duration-100 focus:outline-none',
|
|
218
218
|
].join(' ')}
|
|
219
219
|
aria-label="Delete prompt"
|
|
220
220
|
>
|
|
221
|
-
|
|
221
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
222
|
+
<polyline points="3 6 5 6 21 6" />
|
|
223
|
+
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
|
224
|
+
</svg>
|
|
222
225
|
</button>
|
|
223
226
|
)}
|
|
224
227
|
</div>
|
|
@@ -34,6 +34,16 @@ export function SessionSection({ session, project, onMutate, defaultExpanded = t
|
|
|
34
34
|
const [draggingId, setDraggingId] = useState<string | null>(null);
|
|
35
35
|
const dragSourceRef = useRef<string | null>(null);
|
|
36
36
|
|
|
37
|
+
async function handleDeleteSession(e: React.MouseEvent) {
|
|
38
|
+
e.stopPropagation();
|
|
39
|
+
try {
|
|
40
|
+
await api.deleteSession(project, session.sessionId);
|
|
41
|
+
onMutate();
|
|
42
|
+
} catch {
|
|
43
|
+
// Silent fail
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
37
47
|
const activePrompts = session.prompts.filter(p => p.status !== 'completed');
|
|
38
48
|
const completedPrompts = session.prompts.filter(p => p.status === 'completed').reverse();
|
|
39
49
|
const pendingCount = session.prompts.filter(p => p.status === 'pending').length;
|
|
@@ -107,11 +117,12 @@ export function SessionSection({ session, project, onMutate, defaultExpanded = t
|
|
|
107
117
|
return (
|
|
108
118
|
<div className="border border-[var(--color-border)] rounded-lg overflow-hidden bg-white/[0.02]">
|
|
109
119
|
{/* Session header */}
|
|
120
|
+
<div className="relative group">
|
|
110
121
|
<button
|
|
111
122
|
type="button"
|
|
112
123
|
onClick={() => setExpanded(v => !v)}
|
|
113
124
|
className={[
|
|
114
|
-
'w-full flex items-center gap-3
|
|
125
|
+
'w-full flex items-center gap-3 pl-4 pr-10 py-3 text-left cursor-pointer',
|
|
115
126
|
'hover:bg-white/5 transition-colors duration-150 focus:outline-none',
|
|
116
127
|
].join(' ')}
|
|
117
128
|
aria-expanded={expanded}
|
|
@@ -136,6 +147,22 @@ export function SessionSection({ session, project, onMutate, defaultExpanded = t
|
|
|
136
147
|
▶
|
|
137
148
|
</span>
|
|
138
149
|
</button>
|
|
150
|
+
<button
|
|
151
|
+
type="button"
|
|
152
|
+
onClick={handleDeleteSession}
|
|
153
|
+
className={[
|
|
154
|
+
'absolute right-2 top-1/2 -translate-y-1/2 p-1.5 rounded cursor-pointer',
|
|
155
|
+
'text-[var(--color-muted)]/40 hover:text-red-400 hover:bg-red-400/10',
|
|
156
|
+
'transition-all duration-100 focus:outline-none',
|
|
157
|
+
].join(' ')}
|
|
158
|
+
aria-label="Delete session"
|
|
159
|
+
>
|
|
160
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round">
|
|
161
|
+
<polyline points="3 6 5 6 21 6" />
|
|
162
|
+
<path d="M19 6v14a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2V6m3 0V4a2 2 0 0 1 2-2h4a2 2 0 0 1 2 2v2" />
|
|
163
|
+
</svg>
|
|
164
|
+
</button>
|
|
165
|
+
</div>
|
|
139
166
|
|
|
140
167
|
{/* Session content */}
|
|
141
168
|
{expanded && (
|