@heyquilt/seam 0.26.0 → 0.27.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 CHANGED
@@ -23,6 +23,7 @@ Docs: https://seam.heyquilt.com/docs
23
23
  seam oauth client new <name> --redirect <uri> | list | revoke <client_id> apps that ask people for access (OAuth 2, PKCE)
24
24
  seam quilts [new <slug> | show <q>] Your quilts (repositories)
25
25
  seam import git <path> [--into <slug>]
26
+ seam github [import <owner/name> --installation <id> [--into <slug>]] from GitHub, followed
26
27
  seam import git <remote-url> --into <slug> [--follow] [--interval <s>] [--credential <name>]
27
28
  seam import status <id> | seam import poke <id> | seam import cutover <id> an import that follows git
28
29
  seam export remote <url> --quilt <q> [--line main,release/*] [--releases] [--credential <name>] | seam export status
package/lib/quilts/cli.js CHANGED
@@ -42,7 +42,7 @@ export function parseTiers (text) {
42
42
 
43
43
  /** `test/**,**\/*.test.js` → a list; `none` clears it. */
44
44
  export const parseGlobs = (text) => text === 'none' ? null : text.split(',').map((x) => x.trim()).filter(Boolean)
45
- const VALUE_FLAGS = new Set(['-m', '--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'])
45
+ const VALUE_FLAGS = new Set(['-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'])
46
46
 
47
47
  export function parse (argv) {
48
48
  const pos = []; const flags = {}; const paths = []
@@ -491,6 +491,18 @@ export async function runSeamCommand (cmd, argv, { cwd = process.cwd(), token, o
491
491
  const r = await client.tree(needQuilt(), pos[0] || 'main', pos.slice(1).concat(paths))
492
492
  return print(r, r.entries.map((e) => `${e.mode}\t${e.blob ? e.blob.slice(0, 12) : e.term.slice(0, 12)}\t${e.path}`).join('\n'))
493
493
  }
494
+ case 'github': {
495
+ // GitHub through the Seam app: what it reaches, and importing (and following) a repository.
496
+ if (pos[0] === 'import') {
497
+ if (!pos[1] || !flags['--installation']) throw new Error('seam github import <owner/name> --installation <id> [--into <slug>] [--no-follow]')
498
+ const r = await client.importGithub({ installation_id: flags['--installation'], repo: pos[1], slug: flags['--into'] || undefined, follow: !flags['--no-follow'] })
499
+ return print(r, `Importing ${pos[1]} (import ${r.id})${r.follow ? ', following it: GitHub tells Seam of every push' : ''}. seam import status ${r.id}`)
500
+ }
501
+ const g = await client.github()
502
+ if (!g.configured) return print(g, 'GitHub is not set up on this Seam server.')
503
+ if (!g.installations.length) return print(g, `No GitHub installations yet: install the Seam app from ${g.app.install_url}`)
504
+ return print(g, g.installations.map((i) => `${i.login} (installation ${i.id})\n${i.repos.map((r) => ` ${r.full_name}${r.private ? ' (private)' : ''}`).join('\n') || ' no repositories'}`).join('\n'))
505
+ }
494
506
  case 'import': {
495
507
  // From a git remote, on the server, and following it (14): status and poke name the import.
496
508
  if (pos[0] === 'cutover') {
@@ -112,6 +112,8 @@ export function quiltClient ({ api, token, fetch: fetchImpl = globalThis.fetch,
112
112
  setExport: (q, b) => call('PUT', `${Q(q)}/export`, b),
113
113
  exportStatus: (q) => call('GET', `${Q(q)}/export`),
114
114
  startImport: (b) => call('POST', '/v1/imports', b),
115
+ github: () => call('GET', '/v1/github'),
116
+ importGithub: (b) => call('POST', '/v1/github/import', b),
115
117
  importStatus: (id) => call('GET', `/v1/imports/${enc(id)}`),
116
118
  cutover: (id, b = {}) => call('POST', `/v1/imports/${enc(id)}/cutover`, b),
117
119
  pokeImport: (id) => call('POST', `/v1/imports/${enc(id)}/poke`),
@@ -12,7 +12,7 @@ import { describeLog, describeShow, describeDiff, describeBlame, describeStatus,
12
12
  import { signWithLocalKey, localKey, makeLocalKey, signingKeyFile } from './signing.js'
13
13
  import { status as blockStatus, commitFolder, amendFolder, syncTo, restore, reset, clean, localChanges } from './block.js'
14
14
 
15
- 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_import_status', 'seam_import_poke', 'seam_import_cutover', 'seam_export_remote', 'seam_export_status', 'seam_restore', 'seam_reset', 'seam_clean', 'seam_live', 'seam_live_set', 'seam_bisect', 'seam_grep', 'seam_search', 'seam_describe', 'seam_merge_base', '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_checks', 'seam_check_rerun']
15
+ 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_poke', 'seam_import_cutover', 'seam_export_remote', 'seam_export_status', 'seam_restore', 'seam_reset', 'seam_clean', 'seam_live', 'seam_live_set', 'seam_bisect', 'seam_grep', 'seam_search', 'seam_describe', 'seam_merge_base', '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_checks', 'seam_check_rerun']
16
16
 
17
17
  /** What every agent is told about source control, in each MCP's instructions. */
18
18
  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. ' +
@@ -46,6 +46,8 @@ export const SEAM_DESCRIPTIONS = {
46
46
  seam_line_freeze: 'Freezes a line (admins): merges into it are refused with line_frozen until seam_line_unfreeze.',
47
47
  seam_line_unfreeze: 'Unfreezes a line (admins): it takes merges again.',
48
48
  seam_tag_list: 'The quilt\'s tags: name, commit, the patch it is, and whether it is a release.',
49
+ seam_github: 'Your GitHub installations of the Seam app and the repositories each reaches (install it from seam.heyquilt.com/github). Import one with seam_github_import.',
50
+ seam_github_import: 'Imports a GitHub repository the Seam app reaches into a new quilt and follows it (GitHub tells Seam of every push), with the installation as its credential: no token to paste.',
49
51
  seam_import_remote: 'Imports a git remote (https:// or ssh) into a new quilt on the server. With follow, it keeps taking new commits on main and release/* every interval (and on seam_import_poke) and freezes those lines until cut-over, because git stays the authority. Returns the import id and its poke secret (shown once).',
50
52
  seam_export_remote: 'Exports lines of a quilt to a git remote (admins) after each merge on them, as fast-forwards that are never forced; releases: true pushes release tags too. While an import follows git, only to a scratch remote (scratch: true).',
51
53
  seam_export_status: 'The export to a git remote: its target, lines, state (idle, ok, retrying, refused), each line\'s last exported patch and sha, and the last error.',
@@ -114,6 +116,8 @@ export const SEAM_INPUTS = {
114
116
  seam_line_freeze: { quilt: QUILT, line: z.string().max(110) },
115
117
  seam_line_unfreeze: { quilt: QUILT, line: z.string().max(110) },
116
118
  seam_tag_list: { quilt: QUILT },
119
+ seam_github: {},
120
+ seam_github_import: { repo: z.string().max(201).describe('owner/name, as seam_github lists it'), installation: z.string().max(20).describe('The installation id seam_github shows for it'), into: z.string().max(63).optional().describe('The new quilt\'s slug (default the repo\'s name)'), follow: z.boolean().optional().describe('Keep following it (default true)') },
117
121
  seam_import_remote: { url: z.string().max(2000).describe('The git remote: https://, ssh:// or user@host:path'), into: z.string().max(63).describe('The new quilt\'s slug'), follow: z.boolean().optional().describe('Keep following main and release/* until cut-over'), interval: z.number().int().min(60).optional().describe('Seconds between follow passes (default 600)'), credential: z.string().max(100).optional().describe('The name of the server secret that reads the remote (user:token or a token)') },
118
122
  seam_export_remote: { quilt: z.string().max(100).optional().describe('The quilt'), target: z.string().max(2000).describe('The git remote'), lines: z.array(z.string().max(100)).max(20).optional().describe('Lines to export (default ["main"]); release/* for every release line'), releases: z.boolean().optional().describe('Push release tags too'), credential: z.string().max(100).optional().describe('The name of the server secret that may push'), scratch: z.boolean().optional().describe('A scratch remote nothing pins a sha from'), enabled: z.boolean().optional().describe('false turns the export off') },
119
123
  seam_export_status: { quilt: z.string().max(100).optional().describe('The quilt') },
@@ -310,6 +314,13 @@ export async function runSeamTool (name, args, { client, found = null }) {
310
314
  case 'seam_line_unfreeze': { const l = await client.unfreezeLine(needQuilt(), args.line); return `${l.name} takes merges again.` }
311
315
  case 'seam_tag_list': { const ts = await client.tags(needQuilt()); return ts.length ? ts.map(describeTag).join('\n') : 'No tags yet.' }
312
316
  case 'seam_release_list': { const rs = await client.releases(needQuilt()); return rs.length ? rs.map(describeRelease).join('\n') : 'No releases yet.' }
317
+ case 'seam_github': {
318
+ const g = await client.github()
319
+ if (!g.configured) return 'GitHub is not set up on this Seam server.'
320
+ if (!g.installations.length) return `No GitHub installations yet: install the Seam app from ${g.app.install_url} (or seam.heyquilt.com/github).`
321
+ return g.installations.map((i) => `${i.login} (installation ${i.id}): ${i.repos.map((r) => r.full_name).join(', ') || 'no repositories'}`).join('\n')
322
+ }
323
+ case 'seam_github_import': { const r = await client.importGithub({ installation_id: args.installation, repo: args.repo, slug: args.into, follow: args.follow !== false }); return `Importing ${args.repo} into ${r.id ? (args.into || args.repo.split('/')[1]) : '?'} (import ${r.id})${r.follow ? ', following it' : ''}.` }
313
324
  case 'seam_import_remote': { const r = await client.startImport({ url: args.url, slug: args.into, follow: !!args.follow, interval: args.interval, credential: args.credential }); return `Importing ${args.url} into ${args.into} (import ${r.id})${r.follow ? `, following every ${r.interval_s} s; poke secret ${r.poke_secret} (shown once)` : ''}.` }
314
325
  case 'seam_export_remote': { const r = await client.setExport(needQuilt(), { target: args.target, lines: args.lines, releases: args.releases, credential: args.credential, scratch: args.scratch, enabled: args.enabled !== false }); return r.enabled ? `Exporting ${r.lines.join(', ')} to ${r.target} after each merge.` : `The export to ${r.target} is off.` }
315
326
  case 'seam_export_status': { const r = await client.exportStatus(needQuilt()); return JSON.stringify({ target: r.target, lines: r.lines, releases: r.releases, enabled: r.enabled, status: r.status }, null, 2) }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@heyquilt/seam",
3
- "version": "0.26.0",
3
+ "version": "0.27.0",
4
4
  "description": "seam: Quilt Source's command. Git's verbs for source control that runs on the server, built for many agents and people at once.",
5
5
  "license": "UNLICENSED",
6
6
  "type": "module",