@heyquilt/seam 0.144.0 → 0.145.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/bin/seam.js +5 -2
- package/lib/quilts/block.js +15 -3
- package/lib/quilts/cli.js +29 -8
- package/lib/quilts/client.js +6 -0
- package/lib/quilts/describe.js +48 -5
- package/lib/quilts/tools.js +18 -9
- package/package.json +1 -1
package/bin/seam.js
CHANGED
|
@@ -21,7 +21,8 @@ Docs: https://seam.heyquilt.com/docs
|
|
|
21
21
|
seam logout | seam whoami
|
|
22
22
|
seam computers [list | signout <id or name>] computers signed in to your account (seam login)
|
|
23
23
|
seam handle [<new>] your name in your repos' addresses (<handle>/<repo>); with <new>, change it
|
|
24
|
-
seam account delete --confirm <your email> delete your Seam account (your patches stay)
|
|
24
|
+
seam account delete --confirm <your email> delete your Seam account (your patches stay; repos you deleted are purged)
|
|
25
|
+
seam account log [-n <count>] [--before <id>] what happened to your repos: deleted, restored, purged
|
|
25
26
|
seam agent new <name> | list | key <name> | revoke <name> | relink [agent:<id>] your agents and their keys
|
|
26
27
|
seam oauth client new <name> --redirect <uri> | list | revoke <client_id> apps that ask people for access (OAuth 2, PKCE)
|
|
27
28
|
seam oauth allowed [list | revoke <grant id>] the apps you allowed into your account, and ending one
|
|
@@ -100,6 +101,8 @@ Docs: https://seam.heyquilt.com/docs
|
|
|
100
101
|
seam org team <org> <name> [<member>...] an org's team, named org_team:<name> in its quilts' protections
|
|
101
102
|
seam quilts new <slug> --org <org> | quilts move --quilt <q> --org <org> a quilt that belongs to an org
|
|
102
103
|
seam quilts rename <new-slug> --quilt <q> [--title <name>] a new address (<owner>/<new-slug>); the old one stays an alias
|
|
104
|
+
seam quilts delete <q> --confirm <owner/name> delete a repo (its owner, or an org's admin); restorable for 7 days, then purged
|
|
105
|
+
seam quilts deleted [--org <org>] | restore <slug> [--org <org>] Recently deleted, and bringing one back as it was
|
|
103
106
|
seam quilts agents-make-lines on|off whether agents may make lines, tags and releases
|
|
104
107
|
seam quilts public on|off anyone may read the quilt, signed in or not
|
|
105
108
|
seam quilts add <email|member> [--level read|write|admin] add a member; an email with no account yet is invited
|
|
@@ -141,7 +144,7 @@ async function main () {
|
|
|
141
144
|
const r = await go('PUT', '/v1/me/handle', { handle: argv[0] })
|
|
142
145
|
return console.log(`Your handle is ${r.handle}: your repos are ${r.handle}/<repo> now, and their old addresses stop working.`)
|
|
143
146
|
}
|
|
144
|
-
if (cmd === 'account') {
|
|
147
|
+
if (cmd === 'account' && argv[0] !== 'log') {
|
|
145
148
|
const i = argv.indexOf('--confirm')
|
|
146
149
|
if (argv[0] !== 'delete' || i < 0 || !argv[i + 1]) fail('seam account delete --confirm <your email>')
|
|
147
150
|
const r = await signedIn()('DELETE', `/v1/me?confirm=${encodeURIComponent(argv[i + 1])}`)
|
package/lib/quilts/block.js
CHANGED
|
@@ -892,10 +892,22 @@ export async function pull (client, dir, block) {
|
|
|
892
892
|
const s = await toLineTip(client, dir, block, sq.line, tip)
|
|
893
893
|
return { left: { square: short, state: sq.state }, line: sq.line, at: tip, written: s.written, merged: s.merged, conflicted: s.conflicted }
|
|
894
894
|
}
|
|
895
|
+
// The square moved on the server since this folder last looked (the site's Merge brought its
|
|
896
|
+
// line in, conflicts kept in its new head; or a commit from another folder): the folder takes
|
|
897
|
+
// that head first, markers and all, as seam checkout would. Uncommitted edits stay (syncTo).
|
|
898
|
+
const caught = (sq.head || null) !== (block.head || null) ? { from: block.head || null, ...await syncTo(client, dir, block, sq.head || sq.base, { square: sq }) } : null
|
|
895
899
|
const r = await client.rebase(block.quilt, block.square, { expect_head: block.head })
|
|
896
|
-
if (r.noop) return { rebased: false, noop: true, square: r.square }
|
|
897
|
-
const s = await syncTo(client, dir, block, r.square.head || r.square.base, { square: r.square })
|
|
898
|
-
|
|
900
|
+
if (r.noop && !caught) return { rebased: false, noop: true, square: r.square }
|
|
901
|
+
const s = r.noop ? { written: 0, merged: [], conflicted: [] } : await syncTo(client, dir, block, r.square.head || r.square.base, { square: r.square })
|
|
902
|
+
const both = (k) => [...new Set([...(caught?.[k] || []), ...s[k]])].sort(cmpPath)
|
|
903
|
+
const conflicted = both('conflicted')
|
|
904
|
+
// The conflicts the square itself holds (terms in its head), apart from the folder's own clashes.
|
|
905
|
+
const held = Object.entries(block.files).filter(([p, f]) => f.term && !conflicted.includes(p)).map(([p]) => p).sort(cmpPath)
|
|
906
|
+
return {
|
|
907
|
+
rebased: !r.noop, square: r.square, ...(r.noop ? {} : { result: r }),
|
|
908
|
+
...(caught ? { caught_up: { from: caught.from, to: sq.head || sq.base } } : {}),
|
|
909
|
+
written: (caught?.written || 0) + s.written, merged: both('merged'), conflicted, held
|
|
910
|
+
}
|
|
899
911
|
}
|
|
900
912
|
if (!isLineCheckout(block)) throw new Error('This folder holds one revision, not a line: seam checkout <line or square> moves it.')
|
|
901
913
|
const to = (await client.show(block.quilt, block.line, { format: 'none' })).commit.id
|
package/lib/quilts/cli.js
CHANGED
|
@@ -9,7 +9,7 @@ import { quiltClient } from './client.js'
|
|
|
9
9
|
import { runChecks, readRunnerConfig, listenForWork } from './runner.js'
|
|
10
10
|
import { startDaemon, askDaemon } from './daemon.js'
|
|
11
11
|
import { rebuild as rebuildBlock, conflictStyle } from './block.js'
|
|
12
|
-
import { describeRebuild, describeOpened, describeBoth } from './describe.js'
|
|
12
|
+
import { describeRebuild, describeOpened, describeBoth, describePull, describeDeletedQuilt, describeDeletedQuilts, describePersonLog } from './describe.js'
|
|
13
13
|
import { findBlock, checkout, status as blockStatus, commitFolder, amendFolder, syncTo, localDiff, saveBlock, pull, toLineTip, restore, reset, clean, localChanges, bytesOf, livePatch, resolvePulled } from './block.js'
|
|
14
14
|
import { describeLog, describeShow, describeDiff, describeBlame, describeStatus, describeMerge, describeWrite, describeSquare, describeSquareStatus, describeEvents, describeTag, describeRelease, describeImport, describeImportRecord, describeRestore, describeReset, describeClean, describeLive, describeClaims, describeHandoff, describeFinished, describeResolutions, describeLiveSet, describeLiveJoin, describeMergeBase, describeStack, describeStackDiff, describeGrep, describeBisect, describeCheck, describeReviews, describeInterdiff, describeReviewed, describeSquareCheck, describePolicy, describeKeys, describeTokens, describeNewToken, describeBlock, describeBlocks, describeProtections, describeTeams, describeAccess, describeRedacted, describeRedactions, describeMetrics, describeSquarePolicy, describePolicyVersions, describeOrg, describeOrgs, describeOrgLog, describeOrgDomain, describeApp, describeApps, describeAppKey, describeInstall, describeInstalls, describeRequest, describeRequests, describeOrgTeams, describeCheckRuns, describeRerun, describeAncestor, describeContains, describePipeline, describeWatches, describeSearch, describePublicReads } from './describe.js'
|
|
15
15
|
import { verifyQuilt, describeVerify } from './attest.js'
|
|
@@ -21,7 +21,7 @@ import { signWithLocalKey, localKey, makeLocalKey, signingKeyFile } from './sign
|
|
|
21
21
|
export const API_URL = 'https://api.seam.heyquilt.com'
|
|
22
22
|
|
|
23
23
|
/** The verbs `seam` answers. */
|
|
24
|
-
export const SEAM_VERBS = new Set(['quilts', 'square', 'checkout', 'status', 'commit', 'log', 'show', 'diff', 'merge', 'rebase', 'revert', 'cherry-pick', 'blame', 'resolve', 'cat', 'ls', 'import', 'export', 'events', 'tag', 'release', 'line', 'restore', 'reset', 'clean', 'live', 'bisect', 'grep', 'describe', 'merge-base', 'stack', 'claim', 'unclaim', 'check', 'checks', 'resolutions', 'review', 'sign', 'key', 'token', 'block', 'runner', 'protect', 'team', 'verify', 'redact', 'metrics', 'seal', 'policy', 'org', 'app', 'watch', 'search', 'contains', 'daemon', 'clone', 'pull'])
|
|
24
|
+
export const SEAM_VERBS = new Set(['quilts', 'square', 'checkout', 'status', 'commit', 'log', 'show', 'diff', 'merge', 'rebase', 'revert', 'cherry-pick', 'blame', 'resolve', 'cat', 'ls', 'import', 'export', 'events', 'tag', 'release', 'line', 'restore', 'reset', 'clean', 'live', 'bisect', 'grep', 'describe', 'merge-base', 'stack', 'claim', 'unclaim', 'check', 'checks', 'resolutions', 'review', 'sign', 'key', 'token', 'block', 'runner', 'protect', 'team', 'verify', 'redact', 'metrics', 'seal', 'policy', 'org', 'app', 'watch', 'search', 'contains', 'daemon', 'clone', 'pull', 'account'])
|
|
25
25
|
|
|
26
26
|
/** `ci=agent:ci,lint=agent:a+agent:b` → { ci: ['agent:ci'], lint: ['agent:a', 'agent:b'] }; `none` clears them. */
|
|
27
27
|
export function parseRunners (text) {
|
|
@@ -83,7 +83,7 @@ export function agentTokenExtras (flags) {
|
|
|
83
83
|
return out
|
|
84
84
|
}
|
|
85
85
|
|
|
86
|
-
const VALUE_FLAGS = new Set(['--repos', '--name', '--org', '--description', '--homepage', '--callback', '--setup', '--webhook', '--events', '--perm', '--authors', '--debounce', '-m', '--installation', '--quilts', '--message', '-n', '--max-count', '--into', '--onto', '--on-conflict', '--quilt', '--square', '--dir', '--line', '--task', '--level', '--title', '--slug', '--format', '--expect-tip', '--side', '--file', '--batch', '--to', '--note', '--after', '--kinds', '--webhook', '--secret', '--branch', '--from', '-S', '--run', '--marks', '--patch', '--on', '--path', '--checks', '--name', '--public-key', '--interval', '--credential', '--as', '--runners', '--run-key', '--state', '--tier', '--tiers', '--tests', '--ci-config', '--commit', '--check', '--log-url', '--at', '--human-only', '--keys', '--reason', '--without', '--allow', '--rules', '--org', '--role', '--agent', '--paths', '--squares', '--hours', '--pre-merge-checks', '--by', '--mode', '--machine', '--add', '--remove', '--exclude', '--max-fetch-bytes', '--expires', '--out', '--auto-combine', '--needs-stack', '--monotonic-stack', '--migrations-in-order', '--rule', '--bisect-width', '--classes', '--min-lines', '-L', '--ignore-rev', '--reverse', '--grep', '-G', '--author', '--on-behalf-of', '--since', '--until', '--group-by', '--cursor', '-U', '--unified', '--renames', '--config', '--only', '--listen', '--idle-exit', '--wait', '--days', '--with', '--stack', '--scope', '--suggest', '--to-line', '--approval-survives', '--stack-defaults', '--stack-keys', '--lfs-url', '--lfs-threshold', '--offset', '--length', '--rate', '--before'])
|
|
86
|
+
const VALUE_FLAGS = new Set(['--repos', '--name', '--org', '--description', '--homepage', '--callback', '--setup', '--webhook', '--events', '--perm', '--authors', '--debounce', '-m', '--installation', '--quilts', '--message', '-n', '--max-count', '--into', '--onto', '--on-conflict', '--quilt', '--square', '--dir', '--line', '--task', '--level', '--title', '--slug', '--format', '--expect-tip', '--side', '--file', '--batch', '--to', '--note', '--after', '--kinds', '--webhook', '--secret', '--branch', '--from', '-S', '--run', '--marks', '--patch', '--on', '--path', '--checks', '--name', '--public-key', '--interval', '--credential', '--as', '--runners', '--run-key', '--state', '--tier', '--tiers', '--tests', '--ci-config', '--commit', '--check', '--log-url', '--at', '--human-only', '--keys', '--reason', '--without', '--allow', '--rules', '--org', '--role', '--agent', '--paths', '--squares', '--hours', '--pre-merge-checks', '--by', '--mode', '--machine', '--add', '--remove', '--exclude', '--max-fetch-bytes', '--expires', '--out', '--auto-combine', '--needs-stack', '--monotonic-stack', '--migrations-in-order', '--rule', '--bisect-width', '--classes', '--min-lines', '-L', '--ignore-rev', '--reverse', '--grep', '-G', '--author', '--on-behalf-of', '--since', '--until', '--group-by', '--cursor', '-U', '--unified', '--renames', '--config', '--only', '--listen', '--idle-exit', '--wait', '--days', '--with', '--stack', '--scope', '--suggest', '--to-line', '--approval-survives', '--stack-defaults', '--stack-keys', '--lfs-url', '--lfs-threshold', '--offset', '--length', '--rate', '--before', '--confirm'])
|
|
87
87
|
|
|
88
88
|
export function parse (argv) {
|
|
89
89
|
const pos = []; const flags = {}; const paths = []
|
|
@@ -153,6 +153,24 @@ export async function runSeamCommand (cmd, argv, { cwd = process.cwd(), token, o
|
|
|
153
153
|
const r = await client.renameQuilt(needQuilt(), { ...(pos[1] ? { slug: pos[1] } : {}), ...(flags['--title'] ? { name: flags['--title'] } : {}) })
|
|
154
154
|
return print(r, r.slug === r.was ? `${r.slug} is now called ${r.name}.` : `${r.was} is now ${r.path || r.slug}${r.name !== r.slug ? ` (${r.name})` : ''}. Links by its id keep working; its old address doesn't. In a block folder of it, nothing changes: the block names it by id.`)
|
|
155
155
|
}
|
|
156
|
+
if (pos[0] === 'delete') {
|
|
157
|
+
// seam quilts delete <q> --confirm <owner/name> (11.13): hidden at once, restored for 7 days, purged after.
|
|
158
|
+
const q = pos[1] || quilt
|
|
159
|
+
if (!q || !flags['--confirm']) throw new Error('seam quilts delete <q> --confirm <owner/name>: type the repo\'s address to delete it')
|
|
160
|
+
const r = await client.deleteQuilt(q, flags['--confirm'])
|
|
161
|
+
return print(r, describeDeletedQuilt(r))
|
|
162
|
+
}
|
|
163
|
+
if (pos[0] === 'deleted') {
|
|
164
|
+
// seam quilts deleted [--org <org>]: Recently deleted, yours or an org's (its admins and owners).
|
|
165
|
+
const r = await client.deletedQuilts({ org: flags['--org'] })
|
|
166
|
+
return print(r, describeDeletedQuilts(r, { org: flags['--org'] }))
|
|
167
|
+
}
|
|
168
|
+
if (pos[0] === 'restore') {
|
|
169
|
+
// seam quilts restore <slug|id|owner/name> [--org <org>]: back as it was, within 7 days of the delete.
|
|
170
|
+
if (!pos[1]) throw new Error('seam quilts restore <slug, id or owner/name> [--org <org>]')
|
|
171
|
+
const r = await client.undeleteQuilt(pos[1], { org: flags['--org'] })
|
|
172
|
+
return print(r, `Restored ${r.path || r.quilt}: it is back as it was. The squares the delete closed stay closed; seam square reopen <sq> --quilt ${r.quilt} brings one back.`)
|
|
173
|
+
}
|
|
156
174
|
if (pos[0] === 'sealed') {
|
|
157
175
|
if (!['on', 'off'].includes(pos[1])) throw new Error('seam quilts sealed on|off --quilt <q>')
|
|
158
176
|
const r = await client.setSealed(needQuilt(), pos[1] === 'on')
|
|
@@ -351,12 +369,9 @@ export async function runSeamCommand (cmd, argv, { cwd = process.cwd(), token, o
|
|
|
351
369
|
}
|
|
352
370
|
case 'pull': {
|
|
353
371
|
if (!found) throw new Error('seam pull works in a block folder: run it in a folder made by seam clone or seam checkout.')
|
|
372
|
+
const was = { ...block }
|
|
354
373
|
const r = await pull(client, found.dir, block)
|
|
355
|
-
|
|
356
|
-
if (r.noop) return print(r, block.square ? `Already up to date: ${block.square_short || block.square} sits on ${block.line}'s tip.` : `Already up to date with ${block.line}.`)
|
|
357
|
-
if (r.left) return print(r, `${r.left.square} was ${r.left.state}; the folder is now on ${r.line} (${r.at.slice(0, 12)}, ${r.written} file(s) updated).` + kept)
|
|
358
|
-
if (r.rebased) return print(r, describeWrite(r.result, 'Rebased') + `\n${r.written} file(s) updated in the folder.` + kept)
|
|
359
|
-
return print(r, `Pulled ${r.line} (now ${r.at.slice(0, 12)}): ${r.written} file(s) updated.` + kept)
|
|
374
|
+
return print(r, describePull(r, was))
|
|
360
375
|
}
|
|
361
376
|
case 'checkout': {
|
|
362
377
|
const target = pos[0]
|
|
@@ -689,6 +704,12 @@ export async function runSeamCommand (cmd, argv, { cwd = process.cwd(), token, o
|
|
|
689
704
|
const r = await client.editApp(pos[1], b)
|
|
690
705
|
return print(r, describeApp(r))
|
|
691
706
|
}
|
|
707
|
+
case 'account': {
|
|
708
|
+
// seam account log [-n <count>] [--before <id>]: what happened to your repos (11.13). seam account delete is the seam package's.
|
|
709
|
+
if (pos[0] !== 'log') throw new Error('seam account log [-n <count>] [--before <id>] | seam account delete --confirm <your email>')
|
|
710
|
+
const r = await client.personLog({ limit: flags['-n'], before: flags['--before'] })
|
|
711
|
+
return print(r, describePersonLog(r))
|
|
712
|
+
}
|
|
692
713
|
case 'org': {
|
|
693
714
|
// seam org [list] | new <slug> [--title <name>] | show <org> | add <org> <member> [--role member|admin|owner] | remove <org> <member>
|
|
694
715
|
const sub = pos[0] || 'list'
|
package/lib/quilts/client.js
CHANGED
|
@@ -237,6 +237,7 @@ export function quiltClient ({ api, token, fetch: fetchImpl = globalThis.fetch,
|
|
|
237
237
|
search: (o = {}) => call('GET', `/v1/search${qs(o)}`),
|
|
238
238
|
searchCode: (q, o = {}) => call('GET', `${Q(q)}/search${qs(o)}`),
|
|
239
239
|
bisect: (q, b) => call('POST', `${Q(q)}/bisect`, b),
|
|
240
|
+
lineLasts: (q, line = 'main', paths = []) => call('GET', `${Q(q)}/lines/${encodeURIComponent(line)}/lasts${qs({ paths: paths.join(',') })}`),
|
|
240
241
|
linePatches: (q, line = 'main', o = {}) => call('GET', `${Q(q)}/lines/${encodeURIComponent(line)}/patches${qs(o)}`),
|
|
241
242
|
diff: (q, o = {}) => call('GET', `${Q(q)}/diff${qs(o)}`),
|
|
242
243
|
resolutions: async (q, p, { hunks = false } = {}) => (await call('GET', `${Q(q)}/resolutions${qs({ path: p || undefined, hunks: hunks ? 1 : undefined })}`)).resolutions,
|
|
@@ -346,6 +347,11 @@ export function quiltClient ({ api, token, fetch: fetchImpl = globalThis.fetch,
|
|
|
346
347
|
setOrgMember: (o, member, role) => String(member).includes('@') ? call('POST', `/v1/orgs/${enc(o)}/members`, { email: member, role }) : call('PUT', `/v1/orgs/${enc(o)}/members/${enc(member)}`, { role }),
|
|
347
348
|
setQuiltOwner: (q, org) => call('PUT', `${Q(q)}/owner`, { org }),
|
|
348
349
|
renameQuilt: (q, body) => call('PATCH', Q(q), body),
|
|
350
|
+
// Deleting a repo (11.13): confirm is its address, <owner>/<slug>; restored for 7 days by id, slug or address.
|
|
351
|
+
deleteQuilt: (q, confirm) => call('DELETE', `${Q(q)}${qs({ confirm })}`),
|
|
352
|
+
deletedQuilts: ({ org } = {}) => call('GET', org ? `/v1/orgs/${enc(org)}/deleted-quilts` : '/v1/me/deleted-quilts'),
|
|
353
|
+
undeleteQuilt: (q, { org } = {}) => call('POST', `${org ? `/v1/orgs/${enc(org)}` : '/v1/me'}/deleted-quilts/${enc(q)}/restore`, {}),
|
|
354
|
+
personLog: ({ limit, before } = {}) => call('GET', `/v1/me/log${qs({ limit, before })}`),
|
|
349
355
|
setAgentsMakeLines: (q, allow) => call('PUT', `${Q(q)}/agents-make-lines`, { allow }),
|
|
350
356
|
setPublic: (q, value) => call('PUT', `${Q(q)}/public`, { public: value }),
|
|
351
357
|
setSealed: (q, value) => call('PUT', `${Q(q)}/sealed`, { sealed: value }),
|
package/lib/quilts/describe.js
CHANGED
|
@@ -19,20 +19,22 @@ export function describeSquare (s) {
|
|
|
19
19
|
}
|
|
20
20
|
|
|
21
21
|
export function describeLog (r, { oneline = false, graph = false } = {}) {
|
|
22
|
-
|
|
22
|
+
// A search that ran out of time (stopped: 'time') or of commits to look at ('scan') goes on with its cursor.
|
|
23
|
+
const early = r.partial ? (r.stopped === 'time' ? '(stopped early: the search ran out of time) ' : '(partial) ') : ''
|
|
24
|
+
const more = r.cursor ? (r.partial ? `${early}(search further: seam log … --cursor ${r.cursor})` : `(more: seam log … --cursor ${r.cursor})`) : r.more ? (oneline ? '…' : '(more: seam log -n <count>)') : null
|
|
23
25
|
if (r.groups) {
|
|
24
26
|
// group_by: git shortlog -s, with lines added and removed.
|
|
25
27
|
if (!r.groups.length) return 'No commits.'
|
|
26
28
|
const w = Math.max(...r.groups.map((g) => String(g.commits).length))
|
|
27
|
-
return [...r.groups.map((g) => `${String(g.commits).padStart(w + 2)} ${g.key == null ? '(none)' : g.name ? `${g.name} <${g.key}>` : g.key} +${g.added} -${g.removed}`), ...(more ? [
|
|
29
|
+
return [...r.groups.map((g) => `${String(g.commits).padStart(w + 2)} ${g.key == null ? '(none)' : g.name ? `${g.name} <${g.key}>` : g.key} +${g.added} -${g.removed}`), ...(more ? [more] : [])].join('\n')
|
|
28
30
|
}
|
|
29
|
-
if (!r.commits.length) return more ?
|
|
31
|
+
if (!r.commits.length) return more ? `${r.partial ? 'None found yet.' : 'No commits on this page.'}\n${more}` : r.existed === false ? 'No commits: this path was never here.' : 'No commits.'
|
|
30
32
|
const mark = (c) => graph ? (c.parents.length > 1 ? '* ' : c.hidden ? 'x ' : '* ') : ''
|
|
31
33
|
const merged = (c) => (c.parent_squares || []).map((s) => `${graph ? '|\\ ' : ''}merged ${s.square_id ? `square ${s.square_id.slice(0, 8)}${s.title ? ` "${s.title}"` : ''}` : s.commit.slice(0, 12)}`)
|
|
32
34
|
const lines = r.commits.map((c) => oneline
|
|
33
35
|
? [`${mark(c)}${c.short} ${c.patch ? `[${c.patch.line}:${c.patch.patch_no}] ` : ''}${first(c.message)}${r.follow && c.path && c.path !== r.follow ? ` (${c.path})` : ''}${c.hidden ? ` (hidden: replaced by ${c.predecessor_of.slice(0, 12)})` : ''}${c.signature ? ` (signature: ${c.signature.status})` : ''}`, ...merged(c)].join('\n')
|
|
34
36
|
: [`${mark(c)}commit ${c.id}${c.patch ? ` (${patchName(c.patch)})` : ''}${c.hidden ? ` (hidden: replaced by ${c.predecessor_of.slice(0, 12)})` : ''}`, ...(c.parents.length > 1 ? [`Merge: ${c.parents.map((p) => p.slice(0, 12)).join(' ')}`, ...merged(c)] : []), `Square: ${c.square}`, ...(r.follow && c.path && c.path !== r.follow ? [`Path: ${c.path}`] : []), `Author: ${c.author.name} <${c.author.member_id}>${c.author.tool ? ` via ${c.author.tool}` : ''}${c.on_behalf_of ? ` for ${c.on_behalf_of}` : ''}`, ...(c.task_id ? [`Task: ${c.task_id}`] : []), ...(c.signature ? [describeSignature(c.signature)] : []), `Date: ${when(c.authored_at)}`, '', ...String(c.message || '').split('\n').map((l) => ` ${l}`), ''].join('\n'))
|
|
35
|
-
if (more) lines.push(
|
|
37
|
+
if (more) lines.push(more)
|
|
36
38
|
if (r.last_present !== undefined) lines.push(`This path is gone: it was last here in patch ${r.last_present}.`)
|
|
37
39
|
return lines.join('\n')
|
|
38
40
|
}
|
|
@@ -111,9 +113,24 @@ export function describeBoth (r, { diff = 'seam diff', resolve = 'seam resolve <
|
|
|
111
113
|
const out = []
|
|
112
114
|
if (r.merged?.length) out.push(`Merged upstream's changes into your edits to ${r.merged.join(', ')}: compare with ${diff}.`)
|
|
113
115
|
if (r.conflicted?.length) out.push(`Conflict in ${r.conflicted.join(', ')} (changed on both sides): settle each by editing out the conflict markers, or with ${resolve}, then commit.`)
|
|
116
|
+
if (r.held?.length) out.push(`The square holds a conflict in ${r.held.join(', ')}: settle each by editing out the conflict markers, or with ${resolve}, then commit.`)
|
|
114
117
|
return out.map((l) => '\n' + l).join('')
|
|
115
118
|
}
|
|
116
119
|
|
|
120
|
+
/**
|
|
121
|
+
* seam pull's answer (block.js pull), the same for the CLI and seam mcp. `block` is the folder's
|
|
122
|
+
* record before the pull; `diff` and `resolve` as describeBoth.
|
|
123
|
+
*/
|
|
124
|
+
export function describePull (r, block, names) {
|
|
125
|
+
const kept = describeBoth(r, names)
|
|
126
|
+
if (r.noop) return block.square ? `Already up to date: ${block.square_short || block.square} sits on ${block.line}'s tip.` : `Already up to date with ${block.line}.`
|
|
127
|
+
if (r.left) return `${r.left.square} was ${r.left.state}; the folder is now on ${r.line} (${r.at.slice(0, 12)}, ${r.written} file(s) updated).` + kept
|
|
128
|
+
const caught = r.caught_up ? `Brought ${r.square.short}'s new head in (${String(r.caught_up.to).slice(0, 12)}): the square moved on the server since this folder last looked.` : ''
|
|
129
|
+
if (r.rebased) return (caught ? caught + '\n' : '') + describeWrite(r.result, 'Rebased') + `\n${r.written} file(s) updated in the folder.` + kept
|
|
130
|
+
if (caught) return `${caught} ${r.written} file(s) updated; it sits on ${r.square.line}'s tip.` + kept
|
|
131
|
+
return `Pulled ${r.line} (now ${r.at.slice(0, 12)}): ${r.written} file(s) updated.` + kept
|
|
132
|
+
}
|
|
133
|
+
|
|
117
134
|
export function describeStatus (st) {
|
|
118
135
|
const lines = []
|
|
119
136
|
if (st.square) lines.push(describeSquareStatus(st.square, { prefix: 'On square ' }))
|
|
@@ -121,7 +138,7 @@ export function describeStatus (st) {
|
|
|
121
138
|
else lines.push(`A read-only checkout of ${st.at ? st.at.slice(0, 12) : 'an empty line'}`)
|
|
122
139
|
lines.push(`Block: ${st.paths.map((p) => p || '/').join(', ')} at ${st.at ? st.at.slice(0, 12) : '(empty)'}`)
|
|
123
140
|
if (st.square_closed) lines.push(`This square is ${st.square_closed}: seam pull moves the folder to ${st.square.line}'s tip.`)
|
|
124
|
-
if (st.stale) lines.push('The square moved on since this folder last looked:
|
|
141
|
+
if (st.stale) lines.push('The square moved on since this folder last looked: seam pull brings its new head in (your uncommitted edits stay).')
|
|
125
142
|
if (st.conflicted.length) lines.push(`Conflicted (edit and commit without the markers, or seam resolve --ours|--theirs): ${st.conflicted.join(', ')}`)
|
|
126
143
|
if (!st.changes.length) lines.push('Nothing changed since the last commit.')
|
|
127
144
|
else lines.push('Changes not yet committed:', ...st.changes.map((c) => ` ${c.status.padEnd(9)} ${c.path}`))
|
|
@@ -572,6 +589,31 @@ export const describeInstalls = (r) => r.installations.length ? r.installations.
|
|
|
572
589
|
|
|
573
590
|
export const describeApps = (r) => r.apps.length ? r.apps.map((a) => describeApp(a)).join('\n\n') : 'No apps.'
|
|
574
591
|
|
|
592
|
+
// The rows deleting a repo writes in its owner's log (11.13), the org's and a person's alike.
|
|
593
|
+
const QUILT_LOG = {
|
|
594
|
+
quilt_deleted: (d) => `deleted the repo ${d.slug}${d.closed ? ` (closed ${d.closed} agent square${d.closed === 1 ? '' : 's'})` : ''}`,
|
|
595
|
+
quilt_restored: (d) => `restored the repo ${d.slug}`,
|
|
596
|
+
quilt_purged: (d) => `purged the repo ${d.slug}${d.reason === 'account_deleted' ? ' (the account was deleted)' : ', 7 days after it was deleted'}`
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/** A deleted repo (11.13): when it is purged, and how to bring it back. */
|
|
600
|
+
export function describeDeletedQuilt (r) {
|
|
601
|
+
const org = String(r.owner || '').startsWith('org:') && r.path ? r.path.split('/')[0] : null
|
|
602
|
+
return `Deleted ${r.path || r.quilt}: nobody reaches it now, and its address stays ${org ? `${org}'s` : 'yours'}. Restore it until ${when(r.purge_at)} with seam quilts restore ${r.quilt}${org ? ` --org ${org}` : ''}; after that it is purged.${r.closed_squares ? ` ${r.closed_squares} square${r.closed_squares === 1 ? '' : 's'} agents held closed.` : ''}`
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
/** Recently deleted: yours or an org's, each with when it is purged. */
|
|
606
|
+
export function describeDeletedQuilts (r, { org } = {}) {
|
|
607
|
+
if (!r.quilts.length) return `No deleted repos${org ? ` in ${org}` : ''}: a deleted repo stays here 7 days.`
|
|
608
|
+
return r.quilts.map((q) => `${(q.path || q.slug).padEnd(24)} deleted ${when(q.deleted_at)} by ${q.deleted_by}, purged ${when(q.purge_at)} (seam quilts restore ${q.slug}${org ? ` --org ${org}` : ''})`).join('\n')
|
|
609
|
+
}
|
|
610
|
+
|
|
611
|
+
/** A person's log (11.13): what happened to their repos, newest first. */
|
|
612
|
+
export function describePersonLog (r) {
|
|
613
|
+
if (!r.log.length) return 'Your log is empty.'
|
|
614
|
+
return [...r.log.map((x) => `${x.id.padStart(6)} ${new Date(x.at).toISOString().slice(0, 16).replace('T', ' ')} ${x.actor} ${(QUILT_LOG[x.kind] || ((d) => `${x.kind} ${JSON.stringify(d)}`))(x.data || {})}`), ...(r.next ? [`More: seam account log --before ${r.next}`] : [])].join('\n')
|
|
615
|
+
}
|
|
616
|
+
|
|
575
617
|
export function describeOrgLog (r) {
|
|
576
618
|
const what = (x) => {
|
|
577
619
|
const d = x.data || {}
|
|
@@ -581,6 +623,7 @@ export function describeOrgLog (r) {
|
|
|
581
623
|
if (x.kind.startsWith('app_')) return `${{ app_created: 'made', app_changed: 'changed', app_removed: 'removed', app_manager_added: `made ${d.member} a manager of`, app_manager_removed: `took ${d.member} off the managers of`, app_key_added: `made a key ${d.key_id} for`, app_key_removed: `removed the key ${d.key_id} of`, app_moved_in: `moved in from ${d.from}` }[x.kind] || x.kind}${x.kind.startsWith('app_key') || x.kind.startsWith('app_manager') || x.kind === 'app_moved_in' ? '' : ' the app'} ${d.handle} ${d.handle}${d.fields?.length ? ` (${d.fields.join(', ')})` : ''}`
|
|
582
624
|
if (x.kind === 'app_installed' || x.kind === 'app_uninstalled' || x.kind.startsWith('install_')) return `${{ app_installed: 'installed', app_uninstalled: 'removed', install_requested: 'asked for', install_request_approved: 'approved the request for', install_request_declined: 'declined the request for', install_repos_changed: 'changed the repos of', install_paused: 'paused', install_permissions_accepted: 'accepted new permissions for', install_resumed: 'resumed' }[x.kind] || x.kind} the app ${d.handle}${['app_installed', 'install_repos_changed', 'install_requested'].includes(x.kind) ? ` (${d.repos ? d.repos.join(', ') : 'all repos'})` : ''}`
|
|
583
625
|
if (x.kind.startsWith('domain_')) return `${{ domain_added: 'added', domain_verified: 'verified', domain_lost: 'lost (its record went away)', domain_removed: 'removed' }[x.kind] || x.kind} the domain ${d.domain}`
|
|
626
|
+
if (x.kind.startsWith('quilt_') && QUILT_LOG[x.kind]) return QUILT_LOG[x.kind](d)
|
|
584
627
|
if (x.kind === 'quilt_in') return `moved the quilt ${d.slug} in (from ${d.from})`
|
|
585
628
|
if (x.kind === 'quilt_out') return `moved the quilt ${d.slug} out (to ${d.to})`
|
|
586
629
|
return `${x.kind} ${JSON.stringify(d)}`
|
package/lib/quilts/tools.js
CHANGED
|
@@ -16,10 +16,10 @@ import { eventLine, describeLog, describeShow, describeDiff, describeBlame, desc
|
|
|
16
16
|
import { signWithLocalKey, localKey, makeLocalKey, signingKeyFile } from './signing.js'
|
|
17
17
|
import { startDaemon, askDaemon } from './daemon.js'
|
|
18
18
|
import { rebuild as rebuildBlock, conflictStyle } from './block.js'
|
|
19
|
-
import { describeRebuild, describeOpened, describeBoth } from './describe.js'
|
|
19
|
+
import { describeRebuild, describeOpened, describeBoth, describePull, describeDeletedQuilt, describeDeletedQuilts, describePersonLog } from './describe.js'
|
|
20
20
|
import { status as blockStatus, commitFolder, amendFolder, syncTo, pull, toLineTip, checkout, restore, reset, clean, localChanges, bytesOf, livePatch, resolvePulled } from './block.js'
|
|
21
21
|
|
|
22
|
-
export const SEAM_TOOLS = ['seam_quilts', 'seam_open_square', 'seam_squares', 'seam_log', 'seam_show', 'seam_diff', 'seam_blame', 'seam_cat', 'seam_ls', 'seam_commit', 'seam_merge', 'seam_rebase', 'seam_revert', 'seam_cherry_pick', 'seam_resolve', 'seam_square_status', 'seam_square_handoff', 'seam_abandon', 'seam_square_merged', 'seam_reopen', 'seam_fold', 'seam_split', 'seam_events', 'seam_events_webhook', 'seam_line_list', 'seam_line_new', 'seam_line_freeze', 'seam_line_unfreeze', 'seam_line_require', 'seam_reviews', 'seam_review', 'seam_tag_list', 'seam_tag', 'seam_release_list', 'seam_release', 'seam_import_git', 'seam_import_remote', 'seam_github', 'seam_github_import', 'seam_import_status', 'seam_import_authors', 'seam_import_poke', 'seam_import_cutover', 'seam_export_remote', 'seam_export_status', 'seam_export_push', 'seam_restore', 'seam_reset', 'seam_clean', 'seam_live', 'seam_live_join', 'seam_live_set', 'seam_bisect', 'seam_grep', 'seam_search', 'seam_describe', 'seam_merge_base', 'seam_stack', 'seam_migrations_dir', 'seam_claims', 'seam_claim', 'seam_unclaim', 'seam_check', 'seam_resolutions', 'seam_forget_resolution', 'seam_sign', 'seam_keys', 'seam_key_add', 'seam_key_remove', 'seam_token_list', 'seam_token_new', 'seam_token_revoke', 'seam_protections', 'seam_protections_set', 'seam_team_set', 'seam_verify', 'seam_redact', 'seam_redactions', 'seam_seal', 'seam_secret_scanning', 'seam_public', 'seam_sealed', 'seam_policy', 'seam_line_policy_versions', 'seam_orgs', 'seam_org_log', 'seam_org_domain', 'seam_apps', 'seam_app', 'seam_app_key', 'seam_app_manager', 'seam_app_install', 'seam_app_request', 'seam_org_member', 'seam_org_team', 'seam_agent_token_new', 'seam_agent_token_list', 'seam_agent_token_revoke', 'seam_agents_make_lines', 'seam_checks', 'seam_check_rerun', 'seam_reparent', 'seam_archive', 'seam_unarchive', 'seam_hide', 'seam_unhide', 'seam_square_edit', 'seam_share', 'seam_unshare', 'seam_export_tarball', 'seam_claim_request', 'seam_claim_withdraw', 'seam_claim_handoff', 'seam_claim_finish', 'seam_blocks', 'seam_block_open', 'seam_block_touch', 'seam_block_close', 'seam_ancestor', 'seam_contains', 'seam_pipeline', 'seam_watch', 'seam_unwatch', 'seam_absorb', 'seam_interdiff', 'seam_apply_suggestion', 'seam_infra_paths', 'seam_owner_idle_hours', 'seam_public_reads', 'seam_export_archive', 'seam_metrics', 'seam_daemon', 'seam_block_rebuild', 'seam_clone', 'seam_pull', 'seam_member_add']
|
|
22
|
+
export const SEAM_TOOLS = ['seam_quilts', 'seam_open_square', 'seam_squares', 'seam_log', 'seam_show', 'seam_diff', 'seam_blame', 'seam_cat', 'seam_ls', 'seam_commit', 'seam_merge', 'seam_rebase', 'seam_revert', 'seam_cherry_pick', 'seam_resolve', 'seam_square_status', 'seam_square_handoff', 'seam_abandon', 'seam_square_merged', 'seam_reopen', 'seam_fold', 'seam_split', 'seam_events', 'seam_events_webhook', 'seam_line_list', 'seam_line_new', 'seam_line_freeze', 'seam_line_unfreeze', 'seam_line_require', 'seam_reviews', 'seam_review', 'seam_tag_list', 'seam_tag', 'seam_release_list', 'seam_release', 'seam_import_git', 'seam_import_remote', 'seam_github', 'seam_github_import', 'seam_import_status', 'seam_import_authors', 'seam_import_poke', 'seam_import_cutover', 'seam_export_remote', 'seam_export_status', 'seam_export_push', 'seam_restore', 'seam_reset', 'seam_clean', 'seam_live', 'seam_live_join', 'seam_live_set', 'seam_bisect', 'seam_grep', 'seam_search', 'seam_describe', 'seam_merge_base', 'seam_stack', 'seam_migrations_dir', 'seam_claims', 'seam_claim', 'seam_unclaim', 'seam_check', 'seam_resolutions', 'seam_forget_resolution', 'seam_sign', 'seam_keys', 'seam_key_add', 'seam_key_remove', 'seam_token_list', 'seam_token_new', 'seam_token_revoke', 'seam_protections', 'seam_protections_set', 'seam_team_set', 'seam_verify', 'seam_redact', 'seam_redactions', 'seam_seal', 'seam_secret_scanning', 'seam_public', 'seam_sealed', 'seam_policy', 'seam_line_policy_versions', 'seam_orgs', 'seam_org_log', 'seam_org_domain', 'seam_apps', 'seam_app', 'seam_app_key', 'seam_app_manager', 'seam_app_install', 'seam_app_request', 'seam_org_member', 'seam_org_team', 'seam_agent_token_new', 'seam_agent_token_list', 'seam_agent_token_revoke', 'seam_agents_make_lines', 'seam_checks', 'seam_check_rerun', 'seam_reparent', 'seam_archive', 'seam_unarchive', 'seam_hide', 'seam_unhide', 'seam_square_edit', 'seam_share', 'seam_unshare', 'seam_export_tarball', 'seam_claim_request', 'seam_claim_withdraw', 'seam_claim_handoff', 'seam_claim_finish', 'seam_blocks', 'seam_block_open', 'seam_block_touch', 'seam_block_close', 'seam_ancestor', 'seam_contains', 'seam_pipeline', 'seam_watch', 'seam_unwatch', 'seam_absorb', 'seam_interdiff', 'seam_apply_suggestion', 'seam_infra_paths', 'seam_owner_idle_hours', 'seam_public_reads', 'seam_export_archive', 'seam_metrics', 'seam_daemon', 'seam_block_rebuild', 'seam_clone', 'seam_pull', 'seam_member_add', 'seam_quilt_delete', 'seam_quilts_deleted', 'seam_quilt_restore', 'seam_account_log']
|
|
23
23
|
|
|
24
24
|
/** What every agent is told about source control, in each MCP's instructions. */
|
|
25
25
|
export const SEAM_GUIDE = 'Quilt source control (quilts): a quilt is a repository on the Quilt server; you work in a square (your own branch and working copy), commit save points in it, and merge it into main, where it becomes a patch. ' +
|
|
@@ -85,7 +85,7 @@ export const SEAM_DESCRIPTIONS = {
|
|
|
85
85
|
seam_block_touch: 'The heartbeat of an open block: keeps it open (blocks untouched for seven days are closed) and moves a live block to the square\'s current head; add and remove change its folders.',
|
|
86
86
|
seam_clone: 'Checks out a line of a quilt (main by default) into a new folder on this computer, ready to work: the first commit there opens a square by itself. The folder is named after the quilt unless dir is given.',
|
|
87
87
|
seam_member_add: 'Adds a member to the quilt or changes their level (an admin): email (a person; one with no Seam account yet is invited, and is in the quilt from their first sign-in) or member_id (person:… or agent:…), level read, write or admin (default write; remove takes them out).',
|
|
88
|
-
seam_pull: 'In a block folder: brings the line\'s new patches in (uncommitted edits stay; a file changed on both sides is merged, or given conflict markers to settle before it is committed); on a square, rebases it onto its line\'s tip; on a square merged from elsewhere, moves the folder to the line\'s tip.',
|
|
88
|
+
seam_pull: 'In a block folder: brings the line\'s new patches in (uncommitted edits stay; a file changed on both sides is merged, or given conflict markers to settle before it is committed); on a square, brings in its new head if it moved on the server (a merge on the site, conflicts included), then rebases it onto its line\'s tip; on a square merged from elsewhere, moves the folder to the line\'s tip.',
|
|
89
89
|
seam_block_rebuild: 'In a block folder: finishes a move of the folder (rebase, checkout of its square) that stopped part way, from the journal in .seam; edits made meanwhile are kept as yours. On a whole folder it writes nothing.',
|
|
90
90
|
seam_block_close: 'Closes a block (its row on the server). The square, its work and any files on disk are untouched.',
|
|
91
91
|
seam_token_list: 'The quilt\'s tokens (keys for one quilt, for CI and scripts): id, name, read or write, and whether revoked. Admins only.',
|
|
@@ -97,6 +97,10 @@ export const SEAM_DESCRIPTIONS = {
|
|
|
97
97
|
seam_redact: 'Redacts content from the quilt for good (a person who is an admin): the blob\'s bytes leave every pack, every read gets a tombstone, and the same bytes are refused if added again. History and ids stay. Name blob, or path (and rev). Cannot be undone: confirm must be true.',
|
|
98
98
|
seam_seal: 'A sealed quilt\'s key (the server never sees its contents): status (key version, who holds it), or grant member: wrap the key this computer holds for that member\'s computers.',
|
|
99
99
|
seam_secret_scanning: 'Reads, or (a person who is an admin) sets, secret scanning on the quilt: refuse (a commit that looks like it holds a secret is refused with path, line and finding id), warn, or off.',
|
|
100
|
+
seam_quilt_delete: 'Deletes a repo (a person: its owner, or an owner or admin of the org that owns it; never an agent on its own). confirm is the repo\'s address as <owner>/<name>, typed by the person who asked. It is hidden at once (it answers as a missing repo; its tokens, webhooks and app installs stop; agents\' open squares in it close) and can be restored for 7 days with seam_quilt_restore; then it is purged. Export it first if anything in it is wanted (seam export archive, or the export to git).',
|
|
101
|
+
seam_quilts_deleted: 'Lists Recently deleted repos: yours, or an org\'s (org: its slug; its admins and owners), each with when it was deleted, by whom, and when it is purged.',
|
|
102
|
+
seam_quilt_restore: 'Restores a repo deleted in the last 7 days, as it was (its address, members, settings, tokens and people\'s squares; the squares the delete closed stay closed, seam_reopen brings one back). quilt: its slug, id or address; org: when an org owned it.',
|
|
103
|
+
seam_account_log: 'What happened to your repos, newest first: deleted, restored, purged; who did it and when (an org\'s are in seam_org_log). before: a row id, for the page after.',
|
|
100
104
|
seam_public: 'Makes the quilt readable by anyone, signed in or not (public: true), or private again (a person who is an admin). The quilt\'s protections can still keep paths out of public view.',
|
|
101
105
|
seam_policy: 'A square\'s change classes (whitespace, comment-only, docs, test-only, dependency, ci-config, schema, logic, per file) and what its line\'s rules by path and class need before it merges: approvals from whom, checks, and which are met.',
|
|
102
106
|
seam_line_policy_versions: 'A line\'s policy versions (every change to what it needs is one), or, for a person with admin: approve or reject a pending version someone else proposed, or confirm your own with this computer\'s signing key when you are the only admin. Agents read them only.',
|
|
@@ -141,7 +145,7 @@ export const SEAM_DESCRIPTIONS = {
|
|
|
141
145
|
seam_absorb: 'Puts your square\'s uncommitted live work into the commit of the square that last changed each file (amending it and rewriting the commits above it), so a fix to commit 2 of 5 needs no manual rebase. Files no commit of the square changed stay live.',
|
|
142
146
|
seam_fold: 'Folds a square (square) into another square you hold (into): its work becomes one commit there and the square closes. Live work it shared (seam_live_set) is committed on it first and goes along (commit_live: false refuses with live_work instead). Overlapping changes become conflict markers in the target.',
|
|
143
147
|
seam_split: 'Splits your square: by paths (groups: [[paths], ...]) or by claim (one group per claim on the square that covers a changed path; the claim and its holder go with it). Each group moves to a new square (same base) as one commit; the largest group, and paths no group names, stay in this square. stacked: true chains the new squares on this one in order instead of opening them side by side.',
|
|
144
|
-
seam_log: 'History, newest first: rev (default main, or your square, or a..b), paths to limit it, limit (default 20). follow: true with one path follows the file through its renames; search: "<text>" keeps only commits that changed how many times the text appears (git log -S); match: "<regex>" keeps commits whose added or removed lines match (git log -G). message (with message_regex, ignore_case) filters on the commit message (git log --grep); author (member id or part of a name), on_behalf_of, task, since and until (an ISO date, or 2h, 3d, 1w) filter on who, what for and when. group_by (author, on_behalf_of, task, tool) gives counts with lines added and removed instead of commits; graph: true names the square each merge brought in; include_hidden: true shows a square\'s commits replaced by an amend. When there is more, pass the answer\'s cursor for the next page. Each patch on main is one line\'s merge. With one path on a line, existed says whether it was ever there and, if it is gone now, last_present is the last patch it was there in.',
|
|
148
|
+
seam_log: 'History, newest first: rev (default main, or your square, or a..b), paths to limit it, limit (default 20). follow: true with one path follows the file through its renames; search: "<text>" keeps only commits that changed how many times the text appears (git log -S); match: "<regex>" keeps commits whose added or removed lines match (git log -G). message (with message_regex, ignore_case) filters on the commit message (git log --grep); author (member id or part of a name), on_behalf_of, task, since and until (an ISO date, or 2h, 3d, 1w) filter on who, what for and when. group_by (author, on_behalf_of, task, tool) gives counts with lines added and removed instead of commits; graph: true names the square each merge brought in; include_hidden: true shows a square\'s commits replaced by an amend. When there is more, pass the answer\'s cursor for the next page. A search (search, match, a regex message) looks for about a second: past that it answers what it found so far, says it stopped early, and gives a cursor to search further. Each patch on main is one line\'s merge. With one path on a line, existed says whether it was ever there and, if it is gone now, last_present is the last patch it was there in.',
|
|
145
149
|
seam_describe: 'Names a revision (rev, default main) by the nearest tag behind it: v1.0 on the tag itself, v1.0-3-<commit> three commits past it, the commit alone when no tag is behind it.',
|
|
146
150
|
seam_merge_base: 'Where two revisions (a, b: lines, squares, patches, commits) last shared history: the commit a merge of one into the other starts from.',
|
|
147
151
|
seam_stack: 'The full-stack pointers at a revision (a line, patch:n, sq:…, a commit): each key with its value, where it was set (a patch number or c:<commit>) and how (set by a commit, or derived: db_migration is the newest .sql file under supabase/migrations). Read it to know which schema and database the code at a patch runs against.',
|
|
@@ -249,6 +253,10 @@ export const SEAM_INPUTS = {
|
|
|
249
253
|
seam_line_policy_versions: { quilt: QUILT, line: z.string().max(120), action: z.enum(['list', 'approve', 'reject', 'confirm']).optional(), version: z.number().int().min(1).optional() },
|
|
250
254
|
seam_policy: { quilt: QUILT, square: z.string().max(100).optional(), line: z.string().max(120).optional() },
|
|
251
255
|
seam_public: { quilt: QUILT, public: z.boolean() },
|
|
256
|
+
seam_quilt_delete: { quilt: QUILT, confirm: z.string().max(200).describe('The repo\'s address, <owner>/<name>, exactly as the person typed it') },
|
|
257
|
+
seam_quilts_deleted: { org: z.string().max(100).optional().describe('An org you run: its deleted repos') },
|
|
258
|
+
seam_quilt_restore: { quilt: z.string().max(200).describe('The deleted repo: its slug, id or <owner>/<name>'), org: z.string().max(100).optional().describe('The org that owned it') },
|
|
259
|
+
seam_account_log: { limit: z.number().int().min(1).max(500).optional(), before: z.string().regex(/^\d{1,19}$/).optional() },
|
|
252
260
|
seam_sealed: { quilt: QUILT, sealed: z.boolean() },
|
|
253
261
|
seam_secret_scanning: { quilt: QUILT, mode: z.enum(['refuse', 'warn', 'off']).optional() },
|
|
254
262
|
seam_seal: { quilt: QUILT, action: z.enum(['status', 'grant']).optional(), member: z.string().max(200).optional().describe('For grant: the member id to give the key to') },
|
|
@@ -594,12 +602,9 @@ export async function runSeamTool (name, args, { client, found = null, hosted =
|
|
|
594
602
|
}
|
|
595
603
|
case 'seam_pull': {
|
|
596
604
|
if (!found) throw new Error('seam_pull works on a block folder: run seam mcp in a folder made by seam clone or seam checkout.')
|
|
605
|
+
const was = { ...block }
|
|
597
606
|
const r = await pull(client, found.dir, block)
|
|
598
|
-
|
|
599
|
-
if (r.noop) return block.square ? `Already up to date: ${block.square_short || block.square} sits on ${block.line}'s tip.` : `Already up to date with ${block.line}.`
|
|
600
|
-
if (r.left) return `${r.left.square} was ${r.left.state}; the folder is now on ${r.line} (${r.at.slice(0, 12)}, ${r.written} file(s) updated).` + kept
|
|
601
|
-
if (r.rebased) return describeWrite(r.result, 'Rebased') + `\n${r.written} file(s) updated in the folder.` + kept
|
|
602
|
-
return `Pulled ${r.line} (now ${r.at.slice(0, 12)}): ${r.written} file(s) updated.` + kept
|
|
607
|
+
return describePull(r, was, { diff: 'seam_diff', resolve: 'seam_resolve (choose ours or theirs)' })
|
|
603
608
|
}
|
|
604
609
|
case 'seam_block_rebuild': {
|
|
605
610
|
if (!found) throw new Error('seam_block_rebuild works on a block folder: run seam mcp in a folder made by seam checkout.')
|
|
@@ -641,6 +646,10 @@ export async function runSeamTool (name, args, { client, found = null, hosted =
|
|
|
641
646
|
return describePolicyVersions(r)
|
|
642
647
|
}
|
|
643
648
|
case 'seam_orgs': return args.org ? describeOrg(await client.org(args.org)) : describeOrgs(await client.orgs())
|
|
649
|
+
case 'seam_quilt_delete': return describeDeletedQuilt(await client.deleteQuilt(needQuilt(), args.confirm))
|
|
650
|
+
case 'seam_quilts_deleted': return describeDeletedQuilts(await client.deletedQuilts({ org: args.org }), { org: args.org })
|
|
651
|
+
case 'seam_quilt_restore': { const r = await client.undeleteQuilt(args.quilt, { org: args.org }); return `Restored ${r.path || r.quilt}: it is back as it was. The squares the delete closed stay closed; seam_reopen brings one back.` }
|
|
652
|
+
case 'seam_account_log': return describePersonLog(await client.personLog({ limit: args.limit, before: args.before }))
|
|
644
653
|
case 'seam_org_log': return describeOrgLog(await client.orgLog(args.org, { limit: args.limit, before: args.before }))
|
|
645
654
|
case 'seam_apps': return describeApps(await client.apps({ verified: !!args.verified }))
|
|
646
655
|
case 'seam_app': {
|
package/package.json
CHANGED