heyquilt 0.0.1 → 0.3.90
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/LICENSE +85 -0
- package/README.md +630 -2
- package/RELEASES.md +733 -0
- package/assets/logo.svg +1 -0
- package/bin/quilt.js +816 -0
- package/desktop/main.js +315 -0
- package/package.json +163 -7
- package/src/account.js +563 -0
- package/src/admit-policy.js +32 -0
- package/src/agent-join.js +370 -0
- package/src/agent-task-workflow.js +176 -0
- package/src/agents/actions.js +85 -0
- package/src/agents/claude-code.js +180 -0
- package/src/agents/common.js +21 -0
- package/src/agents/cursor-transcripts.js +170 -0
- package/src/agents/cursor.js +404 -0
- package/src/agents/index.js +69 -0
- package/src/ai-vm-signin.js +98 -0
- package/src/ai-vm.js +380 -0
- package/src/api/access.js +75 -0
- package/src/api/activity.js +181 -0
- package/src/api/agent-auth.js +130 -0
- package/src/api/agent-placement.js +157 -0
- package/src/api/audit.js +83 -0
- package/src/api/auth.js +17 -0
- package/src/api/domains.js +30 -0
- package/src/api/file-paths.js +40 -0
- package/src/api/file-store.js +99 -0
- package/src/api/fly-machines.js +76 -0
- package/src/api/github-app.js +172 -0
- package/src/api/http.js +34 -0
- package/src/api/invite-email.js +53 -0
- package/src/api/issues.js +110 -0
- package/src/api/join-text.js +78 -0
- package/src/api/mailer.js +47 -0
- package/src/api/memory-store.js +1083 -0
- package/src/api/org-access.js +54 -0
- package/src/api/permissions.js +71 -0
- package/src/api/relay-cluster.js +251 -0
- package/src/api/relay-machines.js +60 -0
- package/src/api/routes/access-types.js +54 -0
- package/src/api/routes/agent-invites.js +115 -0
- package/src/api/routes/agents.js +157 -0
- package/src/api/routes/ai-vm.js +369 -0
- package/src/api/routes/github.js +380 -0
- package/src/api/routes/grants.js +70 -0
- package/src/api/routes/invites.js +188 -0
- package/src/api/routes/issues.js +47 -0
- package/src/api/routes/join.js +167 -0
- package/src/api/routes/members.js +110 -0
- package/src/api/routes/orgs.js +177 -0
- package/src/api/routes/relay-cluster.js +90 -0
- package/src/api/routes/relay.js +76 -0
- package/src/api/routes/session-invites.js +119 -0
- package/src/api/routes/sessions.js +92 -0
- package/src/api/routes/teams.js +132 -0
- package/src/api/routes/workspace-agents.js +332 -0
- package/src/api/routes/workspace-files.js +277 -0
- package/src/api/routes/workspace-invites.js +196 -0
- package/src/api/routes/workspaces.js +225 -0
- package/src/api/server.js +623 -0
- package/src/api/slugs.js +20 -0
- package/src/api/supabase-store.js +765 -0
- package/src/api/team-access.js +30 -0
- package/src/api/tokens.js +31 -0
- package/src/api/workspace-access.js +62 -0
- package/src/api/workspace-reach.js +60 -0
- package/src/autowork-tools.js +166 -0
- package/src/autowork.js +387 -0
- package/src/blobstore.js +99 -0
- package/src/boxes.js +37 -0
- package/src/branchdocs.js +235 -0
- package/src/branches.js +194 -0
- package/src/catchup.js +111 -0
- package/src/chat-archive.js +85 -0
- package/src/chat-links.js +923 -0
- package/src/clash.js +140 -0
- package/src/cloud-ai.js +50 -0
- package/src/commit-request.js +59 -0
- package/src/commit.js +213 -0
- package/src/connection.js +569 -0
- package/src/control.js +245 -0
- package/src/conversation.js +125 -0
- package/src/doctor.js +156 -0
- package/src/duties.js +303 -0
- package/src/editors.js +228 -0
- package/src/fsutil.js +87 -0
- package/src/git-hook.js +48 -0
- package/src/git.js +147 -0
- package/src/github-commit.js +320 -0
- package/src/gitignore.js +49 -0
- package/src/gitstate.js +900 -0
- package/src/history.js +307 -0
- package/src/hooks.js +371 -0
- package/src/hosted-agents.js +24 -0
- package/src/hosted-client.js +112 -0
- package/src/identity.js +99 -0
- package/src/inbox.js +237 -0
- package/src/integrations.js +221 -0
- package/src/key-cli.js +115 -0
- package/src/largefiles.js +91 -0
- package/src/legacy.js +25 -0
- package/src/mcp-proxy.js +40 -0
- package/src/mcp-room.js +32 -0
- package/src/mcp.js +1261 -0
- package/src/merge3.js +158 -0
- package/src/merges.js +148 -0
- package/src/pass-source.js +176 -0
- package/src/passes.js +64 -0
- package/src/pathrules.js +127 -0
- package/src/persona.js +101 -0
- package/src/presence.js +360 -0
- package/src/private-file.js +41 -0
- package/src/procs.js +70 -0
- package/src/project-rules.js +178 -0
- package/src/protocol.js +92 -0
- package/src/quiet-warnings.js +9 -0
- package/src/relay-ai-vm.js +82 -0
- package/src/relay-ask.js +141 -0
- package/src/relay-ci.js +111 -0
- package/src/relay-clash.js +597 -0
- package/src/relay-cluster.js +454 -0
- package/src/relay-commit.js +887 -0
- package/src/relay-filekey.js +46 -0
- package/src/relay-git.js +274 -0
- package/src/relay-githttp.js +137 -0
- package/src/relay-github.js +109 -0
- package/src/relay-lease.js +137 -0
- package/src/relay-lfs.js +37 -0
- package/src/relay-mcp.js +1679 -0
- package/src/relay-metrics.js +75 -0
- package/src/relay-outbound.js +1144 -0
- package/src/relay-sync.js +719 -0
- package/src/relay-turn-sync.js +307 -0
- package/src/relay-upstream.js +934 -0
- package/src/relay-webhooks.js +111 -0
- package/src/releases.js +131 -0
- package/src/report.js +129 -0
- package/src/runner.js +306 -0
- package/src/same-edit.js +18 -0
- package/src/server.js +4187 -0
- package/src/session-access.js +170 -0
- package/src/session-name.js +15 -0
- package/src/session.js +7361 -0
- package/src/settings.js +46 -0
- package/src/setup.js +184 -0
- package/src/shell-path.js +46 -0
- package/src/status.js +199 -0
- package/src/summarize.js +109 -0
- package/src/sync-words.js +42 -0
- package/src/task-comments.js +92 -0
- package/src/tasks.js +670 -0
- package/src/textdiff.js +20 -0
- package/src/turn-sync-plan.js +142 -0
- package/src/turn-sync.js +191 -0
- package/src/ui/access-form.js +37 -0
- package/src/ui/agent-env-key.js +76 -0
- package/src/ui/agent-guide.js +127 -0
- package/src/ui/agent-kinds.js +175 -0
- package/src/ui/agent-place.js +265 -0
- package/src/ui/app.css +2309 -0
- package/src/ui/app.js +542 -0
- package/src/ui/board.js +465 -0
- package/src/ui/boxes.js +76 -0
- package/src/ui/branches.js +111 -0
- package/src/ui/catchup.js +77 -0
- package/src/ui/changes.js +159 -0
- package/src/ui/chat.js +266 -0
- package/src/ui/cloud-ai-model.js +107 -0
- package/src/ui/cloud-ai.js +221 -0
- package/src/ui/common.js +431 -0
- package/src/ui/csv.js +21 -0
- package/src/ui/diffview.js +74 -0
- package/src/ui/feed-convs.js +39 -0
- package/src/ui/feed.js +174 -0
- package/src/ui/files.js +579 -0
- package/src/ui/fileview.js +123 -0
- package/src/ui/git-rule.js +60 -0
- package/src/ui/home.js +1323 -0
- package/src/ui/index.html +16 -0
- package/src/ui/invite.js +97 -0
- package/src/ui/invites.js +79 -0
- package/src/ui/loom-model.js +516 -0
- package/src/ui/loom.css +349 -0
- package/src/ui/loom.js +753 -0
- package/src/ui/mark.js +104 -0
- package/src/ui/merges.js +158 -0
- package/src/ui/rail-model.js +78 -0
- package/src/ui/rail.js +173 -0
- package/src/ui/releases.js +182 -0
- package/src/ui/schedule.js +156 -0
- package/src/ui/session.js +3394 -0
- package/src/ui/signin.js +75 -0
- package/src/ui/sounds.js +110 -0
- package/src/ui/tool-logo.js +86 -0
- package/src/ui/tree.js +276 -0
- package/src/ui/workspaces.js +542 -0
- package/src/ui-server.js +1272 -0
- package/src/update-check.js +67 -0
- package/src/upstream.js +155 -0
- package/src/webhooks.js +235 -0
- package/src/workspace-cli.js +62 -0
- package/src/workspace-tools.js +340 -0
- package/index.js +0 -2
package/src/ui/feed.js
ADDED
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
// A person's AI conversations, read-only: their prompts, the AI's replies and
|
|
2
|
+
// one-line actions ("Edited src/app.ts"). Each agent session (a Claude Code
|
|
3
|
+
// session, a Cursor chat, ...) is its own conversation, picked from a strip
|
|
4
|
+
// at the top; the feed follows the newest one until the reader picks another.
|
|
5
|
+
import { esc, clock, avatar, I } from './common.js'
|
|
6
|
+
import { toolLogo } from './tool-logo.js'
|
|
7
|
+
import { conversations, pickConversation } from './feed-convs.js'
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Renders the feed into `el` (the scrolling main area). Keeps the reader's
|
|
11
|
+
* scroll position unless they were already at the bottom. `convSel` is the
|
|
12
|
+
* pinned conversation id (undefined: follow the newest).
|
|
13
|
+
*/
|
|
14
|
+
export function renderFeed (el, { entries, person, isMe, color, agent, online, convSel }) {
|
|
15
|
+
const scroller = el.querySelector('.feed-scroll')
|
|
16
|
+
const atBottom = !scroller || scroller.scrollHeight - scroller.scrollTop - scroller.clientHeight < 60
|
|
17
|
+
const prevCount = scroller ? Number(scroller.dataset.count || 0) : 0
|
|
18
|
+
const prevTop = scroller ? scroller.scrollTop : 0
|
|
19
|
+
const prevConv = scroller ? scroller.dataset.conv : undefined
|
|
20
|
+
|
|
21
|
+
const who = isMe ? 'You' : person
|
|
22
|
+
const whose = isMe ? 'your' : `${person}'s`
|
|
23
|
+
const sharing = !agent || agent.sharing !== false
|
|
24
|
+
const working = sharing && agent && agent.status === 'working'
|
|
25
|
+
let body
|
|
26
|
+
let strip = ''
|
|
27
|
+
let shownConv = ''
|
|
28
|
+
let liveShown = true // the shown conversation is the newest, where the AI is working
|
|
29
|
+
|
|
30
|
+
if (!entries.length) {
|
|
31
|
+
let title, hint
|
|
32
|
+
if (!sharing) {
|
|
33
|
+
title = isMe ? 'You paused sharing your AI chat' : `${person} paused sharing`
|
|
34
|
+
hint = isMe ? 'Turn sharing back on in Session settings when you want partners to follow along.' : 'Their AI conversation shows up here when they resume.'
|
|
35
|
+
} else if (agent && agent.status === 'unavailable') {
|
|
36
|
+
title = `${isMe ? 'Your' : `${person}'s`} ${agent.tool || 'AI'} feed isn't available`
|
|
37
|
+
hint = agent.reason || ''
|
|
38
|
+
} else if (isMe) {
|
|
39
|
+
title = 'Nothing from your AI yet'
|
|
40
|
+
hint = 'When you chat with Claude Code or Cursor in this folder, the conversation shows up here for your partners. Actions are shown as one line; command output and file contents never leave your machine.'
|
|
41
|
+
} else {
|
|
42
|
+
title = `No AI activity from ${person} yet`
|
|
43
|
+
hint = online ? `When ${person} chats with Claude Code or Cursor, you'll see it here live.` : `${person} is offline right now.`
|
|
44
|
+
}
|
|
45
|
+
body = `<div class="feed-empty">${avatar(person, color, online)}<div class="t">${esc(title)}</div>${hint ? `<div class="hint">${esc(hint)}</div>` : ''}</div>`
|
|
46
|
+
} else {
|
|
47
|
+
const convs = conversations(entries)
|
|
48
|
+
const conv = pickConversation(convs, convSel)
|
|
49
|
+
const shown = entries.filter((e) => e.kind === 'paused' || e.kind === 'resumed' || (e.conv || '') === conv)
|
|
50
|
+
const parts = []
|
|
51
|
+
let actions = []
|
|
52
|
+
const flush = () => {
|
|
53
|
+
if (!actions.length) return
|
|
54
|
+
parts.push(`<div class="f-actions">${actions.map((a) => `<div class="f-action"><span class="chev">›</span><span>${esc(a.text)}</span></div>`).join('')}</div>`)
|
|
55
|
+
actions = []
|
|
56
|
+
}
|
|
57
|
+
for (const e of shown) {
|
|
58
|
+
if (e.kind === 'paused' || e.kind === 'resumed') {
|
|
59
|
+
flush()
|
|
60
|
+
parts.push(`<div class="feed-divider"><span>${esc(who)} ${e.kind === 'paused' ? 'paused' : 'resumed'} sharing · ${esc(clock(e.ts))}</span></div>`)
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
63
|
+
if (e.kind === 'action') { actions.push(e); continue }
|
|
64
|
+
flush()
|
|
65
|
+
if (e.kind === 'prompt') {
|
|
66
|
+
parts.push(`<div class="f-prompt">${avatar(person, color)}<div class="f-body">
|
|
67
|
+
<div class="head"><b>${esc(who)}</b><span>${esc(clock(e.ts))}</span>${e.summary ? '<span class="tag summary" title="Summarized before sharing">summary</span>' : ''}</div>
|
|
68
|
+
<div class="bubble"><div class="text">${esc(visiblePrompt(e.text))}</div></div></div></div>`)
|
|
69
|
+
} else if (e.kind === 'reply') {
|
|
70
|
+
parts.push(`<div class="f-reply"><div class="head"><span class="ai-badge">${toolLogo(e.tool)}</span><span>${esc(clock(e.ts))}</span>${e.summary ? '<span class="tag summary" title="Summarized before sharing">summary</span>' : ''}</div>
|
|
71
|
+
<div class="md">${markdown(e.text)}</div></div>`)
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
flush()
|
|
75
|
+
if (!sharing) parts.push(`<div class="feed-note">${esc(isMe ? 'You paused sharing' : `${person} paused sharing`)}</div>`)
|
|
76
|
+
body = parts.join('')
|
|
77
|
+
if (convs.length > 1) {
|
|
78
|
+
strip = `<div class="feed-convs" role="tablist" aria-label="Conversations">${convs.map((c, i) => {
|
|
79
|
+
const on = c.conv === conv
|
|
80
|
+
const live = working && i === 0
|
|
81
|
+
return `<button class="conv-chip${on ? ' on' : ''}" role="tab" aria-selected="${on}" data-conv="${esc(c.conv)}" title="${esc(c.tool || '')}${c.tool ? ' · ' : ''}${esc(clock(c.ts))}">${live ? '<span class="pulse"></span>' : ''}<span class="tool">${toolLogo(c.tool)}</span><span class="nm">${esc(c.label)}</span></button>`
|
|
82
|
+
}).join('')}</div>`
|
|
83
|
+
}
|
|
84
|
+
shownConv = conv
|
|
85
|
+
liveShown = conv === convs[0].conv
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const workingHtml = working && liveShown
|
|
89
|
+
? `<div class="f-working"><span class="dots"><i></i><i></i><i></i></span>${esc(isMe ? 'Your AI is working…' : `${whose} AI is working…`)}</div>`
|
|
90
|
+
: ''
|
|
91
|
+
|
|
92
|
+
el.innerHTML = `${strip}<div class="feed-scroll" data-count="${entries.length}" data-conv="${esc(shownConv)}"><div class="feed">${body}${workingHtml}</div></div>
|
|
93
|
+
<button class="btn sm new-activity" hidden>${I.down}<span>New activity</span></button>`
|
|
94
|
+
const s = el.querySelector('.feed-scroll')
|
|
95
|
+
const jump = el.querySelector('.new-activity')
|
|
96
|
+
if (atBottom || !scroller || prevConv !== shownConv) s.scrollTop = s.scrollHeight
|
|
97
|
+
else {
|
|
98
|
+
s.scrollTop = prevTop
|
|
99
|
+
if (entries.length > prevCount) jump.hidden = false
|
|
100
|
+
}
|
|
101
|
+
jump.onclick = () => { s.scrollTo({ top: s.scrollHeight, behavior: 'smooth' }); jump.hidden = true }
|
|
102
|
+
s.addEventListener('scroll', () => { if (s.scrollHeight - s.scrollTop - s.clientHeight < 60) jump.hidden = true })
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Prompts already shared may still contain Cursor's timestamp and user_query wrappers. */
|
|
106
|
+
export function visiblePrompt (text) {
|
|
107
|
+
const raw = String(text || '')
|
|
108
|
+
const queries = [...raw.matchAll(/<user_query>\s*([\s\S]*?)\s*<\/user_query>/gi)].map((m) => m[1].trim()).filter(Boolean)
|
|
109
|
+
if (queries.length) return queries.join('\n\n')
|
|
110
|
+
return raw.replace(/<timestamp>[\s\S]*?<\/timestamp>/gi, '').trim()
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// ------------------------------------------------------------ markdown --
|
|
114
|
+
// Just enough Markdown for AI replies: code blocks, inline code, bold,
|
|
115
|
+
// italics, headings, horizontal rules, lists and links. Everything is escaped first.
|
|
116
|
+
|
|
117
|
+
export function markdown (text) {
|
|
118
|
+
const out = []
|
|
119
|
+
const chunks = String(text).split(/^```/m)
|
|
120
|
+
chunks.forEach((chunk, i) => {
|
|
121
|
+
if (i % 2 === 1) {
|
|
122
|
+
const nl = chunk.indexOf('\n')
|
|
123
|
+
const code = nl === -1 ? '' : chunk.slice(nl + 1)
|
|
124
|
+
out.push(`<pre class="md-code"><code>${esc(code.replace(/\n$/, ''))}</code></pre>`)
|
|
125
|
+
} else {
|
|
126
|
+
out.push(blocks(chunk))
|
|
127
|
+
}
|
|
128
|
+
})
|
|
129
|
+
return out.join('')
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function blocks (text) {
|
|
133
|
+
const lines = text.split('\n')
|
|
134
|
+
const out = []
|
|
135
|
+
let para = []
|
|
136
|
+
let list = null
|
|
137
|
+
const endPara = () => { if (para.length) { out.push(`<p>${inline(para.join('\n'))}</p>`); para = [] } }
|
|
138
|
+
const endList = () => { if (list) { out.push(`<${list.tag}>${list.items.map((x) => `<li>${inline(x)}</li>`).join('')}</${list.tag}>`); list = null } }
|
|
139
|
+
for (const raw of lines) {
|
|
140
|
+
const line = raw.replace(/\s+$/, '')
|
|
141
|
+
const bullet = line.match(/^\s*[-*+]\s+(.*)$/)
|
|
142
|
+
const num = line.match(/^\s*\d+[.)]\s+(.*)$/)
|
|
143
|
+
const head = line.match(/^(#{1,6})\s+(.*)$/)
|
|
144
|
+
if (!line.trim()) { endPara(); endList(); continue }
|
|
145
|
+
if (/^-{3,}\s*$/.test(line)) { endPara(); endList(); out.push('<hr class="md-hr">'); continue }
|
|
146
|
+
if (head) { endPara(); endList(); out.push(`<p class="md-h">${inline(head[2])}</p>`); continue }
|
|
147
|
+
if (bullet || num) {
|
|
148
|
+
endPara()
|
|
149
|
+
const tag = bullet ? 'ul' : 'ol'
|
|
150
|
+
if (list && list.tag !== tag) endList()
|
|
151
|
+
if (!list) list = { tag, items: [] }
|
|
152
|
+
list.items.push((bullet || num)[1])
|
|
153
|
+
continue
|
|
154
|
+
}
|
|
155
|
+
if (list && /^\s{2,}/.test(raw)) { list.items[list.items.length - 1] += ' ' + line.trim(); continue }
|
|
156
|
+
endList()
|
|
157
|
+
para.push(line)
|
|
158
|
+
}
|
|
159
|
+
endPara()
|
|
160
|
+
endList()
|
|
161
|
+
return out.join('')
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function inline (text) {
|
|
165
|
+
return String(text).split('`').map((part, i) => {
|
|
166
|
+
if (i % 2 === 1) return `<code>${esc(part)}</code>`
|
|
167
|
+
let s = esc(part)
|
|
168
|
+
s = s.replace(/\*\*([^*]+)\*\*/g, '<strong>$1</strong>')
|
|
169
|
+
s = s.replace(/(^|[\s(])\*([^*\s][^*]*)\*/g, '$1<em>$2</em>')
|
|
170
|
+
s = s.replace(/!\[([^\]]*)\]\((https?:\/\/[^)\s]+)\)/g, (m, alt, href) => `<img class="md-img" alt="${alt}" src="${href}" loading="lazy">`)
|
|
171
|
+
s = s.replace(/\[([^\]]+)\]\((https?:\/\/[^\s)]+)\)/g, (m, label, href) => `<a href="${href}" target="_blank" rel="noopener noreferrer">${label}</a>`)
|
|
172
|
+
return s.replace(/\n/g, '<br>')
|
|
173
|
+
}).join('')
|
|
174
|
+
}
|