picocode-core 0.9.163 → 0.9.165
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 +1 -1
- package/src/agent.js +2 -1
- package/src/attachments.js +13 -0
- package/src/controller.js +9 -0
- package/src/derive.js +1 -1
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -2,7 +2,7 @@ import { readFile } from 'node:fs/promises'
|
|
|
2
2
|
import { execFile } from 'node:child_process'
|
|
3
3
|
import { promisify } from 'node:util'
|
|
4
4
|
import { compose, scope, model, noToolsCalled, Inherit, getText } from '@prsm/ai'
|
|
5
|
-
import { commitLabel, fileLabel, mediaTypeFor, selectionLabel } from './attachments.js'
|
|
5
|
+
import { commitLabel, elementLabel, fileLabel, mediaTypeFor, selectionLabel } from './attachments.js'
|
|
6
6
|
|
|
7
7
|
const exec = promisify(execFile)
|
|
8
8
|
// a whole patch rides along only while it is small; a bigger commit
|
|
@@ -37,6 +37,7 @@ async function hydratePart(part) {
|
|
|
37
37
|
if (part.type === 'file') return { type: 'text', text: fileLabel(part.path) }
|
|
38
38
|
if (part.type === 'selection') return { type: 'text', text: selectionLabel(part) }
|
|
39
39
|
if (part.type === 'commit') return hydrateCommit(part)
|
|
40
|
+
if (part.type === 'element') return { type: 'text', text: elementLabel(part) }
|
|
40
41
|
if (part.type !== 'image' || part.source?.kind !== 'path') return part
|
|
41
42
|
const mediaType = part.source.mediaType || mediaTypeFor(part.source.path)
|
|
42
43
|
if (!mediaType) return { type: 'text', text: `[image unavailable: ${part.source.path}]` }
|
package/src/attachments.js
CHANGED
|
@@ -73,6 +73,13 @@ export function splitTextByImagePaths(text, exists = existsSync) {
|
|
|
73
73
|
export const fileLabel = (path) => `[file: ${path}]`
|
|
74
74
|
export const selectionLabel = (part) => `[selection: ${part.path}:${part.fromLine}-${part.toLine}]\n${part.text}\n[/selection]`
|
|
75
75
|
export const commitLabel = (part) => `[commit: ${part.hash.slice(0, 7)} ${JSON.stringify(part.subject ?? '')}]`
|
|
76
|
+
// a page element the user picked in a browser: where it is, what it is,
|
|
77
|
+
// and its markup, so the model can find it in the source
|
|
78
|
+
export function elementLabel(part) {
|
|
79
|
+
const styles = part.styles && Object.keys(part.styles).length ? `\ncomputed style: ${Object.entries(part.styles).map(([k, v]) => `${k}: ${v}`).join('; ')}` : ''
|
|
80
|
+
const rules = part.rules?.length ? `\nmatched css rules:\n${part.rules.join('\n')}` : ''
|
|
81
|
+
return `[element: <${part.tag}> ${part.selector} on ${part.url}${part.component ? ` in component ${part.component}` : ''}]\n${part.html}${styles}${rules}\n[/element]`
|
|
82
|
+
}
|
|
76
83
|
|
|
77
84
|
// [Image #n] and [File #n] placeholders in the composed text stand for
|
|
78
85
|
// attachments; they become image, file, or selection parts of the user message
|
|
@@ -89,6 +96,8 @@ export function buildUserContent(text, attachments) {
|
|
|
89
96
|
? { type: 'selection', path: attachment.path, text: attachment.text, fromLine: attachment.fromLine, toLine: attachment.toLine, ...(Number.isInteger(attachment.fromColumn) ? { fromColumn: attachment.fromColumn } : {}), ...(Number.isInteger(attachment.toColumn) ? { toColumn: attachment.toColumn } : {}) }
|
|
90
97
|
: attachment.kind === 'commit'
|
|
91
98
|
? { type: 'commit', hash: attachment.hash, subject: attachment.subject, root: attachment.root }
|
|
99
|
+
: attachment.kind === 'element'
|
|
100
|
+
? { type: 'element', url: attachment.url, selector: attachment.selector, tag: attachment.tag, text: attachment.text, html: attachment.html, rect: attachment.rect, component: attachment.component ?? null, styles: attachment.styles ?? null, rules: attachment.rules ?? [] }
|
|
92
101
|
: attachment.kind === 'file'
|
|
93
102
|
? { type: 'file', path: attachment.path }
|
|
94
103
|
: { type: 'image', source: { kind: 'path', path: attachment.path, mediaType: attachment.mediaType } })
|
|
@@ -131,6 +140,10 @@ export function inputTextFromContent(content, { attachments, nextId }) {
|
|
|
131
140
|
const placeholder = `[File #${nextId()}]`
|
|
132
141
|
attachments.set(placeholder, { path: part.path, kind: 'file' })
|
|
133
142
|
out += placeholder
|
|
143
|
+
} else if (part.type === 'element' && part.selector) {
|
|
144
|
+
const placeholder = `[File #${nextId()}]`
|
|
145
|
+
attachments.set(placeholder, { url: part.url, selector: part.selector, tag: part.tag, text: part.text, html: part.html, rect: part.rect, component: part.component ?? null, styles: part.styles ?? null, rules: part.rules ?? [], kind: 'element' })
|
|
146
|
+
out += placeholder
|
|
134
147
|
} else if (part.type === 'commit' && part.hash) {
|
|
135
148
|
const placeholder = `[File #${nextId()}]`
|
|
136
149
|
attachments.set(placeholder, { hash: part.hash, subject: part.subject, root: part.root, kind: 'commit' })
|
package/src/controller.js
CHANGED
|
@@ -899,6 +899,14 @@ export function createController({ boot }) {
|
|
|
899
899
|
return placeholder
|
|
900
900
|
}
|
|
901
901
|
|
|
902
|
+
// an element picked in a browser page; the message carries its markup
|
|
903
|
+
function attachElement({ url, selector, tag, text = '', html = '', rect = null, component = null, styles = null, rules = [] }) {
|
|
904
|
+
if (!selector || !tag) return null
|
|
905
|
+
const placeholder = `[File #${++state.imageCount}]`
|
|
906
|
+
state.attachments.set(placeholder, { url: String(url ?? ''), selector: String(selector), tag: String(tag), text: String(text).slice(0, 400), html: String(html).slice(0, 4000), rect, component: component ? String(component) : null, styles: styles && typeof styles === 'object' ? styles : null, rules: Array.isArray(rules) ? rules.slice(0, 20).map(String) : [], kind: 'element' })
|
|
907
|
+
return placeholder
|
|
908
|
+
}
|
|
909
|
+
|
|
902
910
|
// a project-relative pick: images attach as images, anything else as a
|
|
903
911
|
// file reference
|
|
904
912
|
function attachProjectFile(file) {
|
|
@@ -1398,6 +1406,7 @@ export function createController({ boot }) {
|
|
|
1398
1406
|
attachFile,
|
|
1399
1407
|
attachSelection,
|
|
1400
1408
|
attachCommit,
|
|
1409
|
+
attachElement,
|
|
1401
1410
|
viewImage: deliverImage,
|
|
1402
1411
|
attachProjectFile,
|
|
1403
1412
|
detachImage,
|
package/src/derive.js
CHANGED
|
@@ -101,7 +101,7 @@ function foldMessage(state, event) {
|
|
|
101
101
|
if (message.role === 'user') {
|
|
102
102
|
const text = Array.isArray(message.content)
|
|
103
103
|
? message.content
|
|
104
|
-
.map((p) => (p.type === 'text' ? p.text : p.type === 'file' ? `[file: ${p.path}]` : p.type === 'selection' ? `[selection: ${p.path}:${p.fromLine}-${p.toLine}]` : p.type === 'commit' ? `[commit: ${String(p.hash).slice(0, 7)}]` : `[image: ${String(p.source?.path || '').split('/').pop() || 'attached'}]`))
|
|
104
|
+
.map((p) => (p.type === 'text' ? p.text : p.type === 'file' ? `[file: ${p.path}]` : p.type === 'selection' ? `[selection: ${p.path}:${p.fromLine}-${p.toLine}]` : p.type === 'commit' ? `[commit: ${String(p.hash).slice(0, 7)}]` : p.type === 'element' ? `[element: ${p.selector}]` : `[image: ${String(p.source?.path || '').split('/').pop() || 'attached'}]`))
|
|
105
105
|
.join('')
|
|
106
106
|
: String(message.content)
|
|
107
107
|
state.transcript.push({ ...base, kind: 'user', text, content: message.content, ...(event.data.origin ? { origin: event.data.origin } : {}) })
|