Files
orca/src/shared/cli-argument-boundary.ts
T
Neil b17d408416 Plugin dev loop: TS workers, hot reload with state, orca plugin dev watch, authoring skill (#36982)
* feat(plugins): build-less TypeScript workers

A worker's main may be .ts/.mts. The plugin host installs module.registerHooks scoped to the
plugin folder: .ts files are transpiled with the frames' sucrase options (lines kept, inline
source map, source maps on), relative imports fall back to the frames' candidate files, and a
value import of the type-only 'orca/worker' fails with a clear error. Needs engines.orca
>=1.4.246.

* feat(plugins): components panels hot reload with their state

- In dev folders a save swaps only the hidden guest: keyed memory atoms are handed to the new
  guest (ui.retain → ui.hydrate memory), dropped when an atom's version changes.
- A save that fails to load keeps the last good render and shows an inline file:line:col
  error banner; the next good save clears it. A subtle Reloaded mark follows a reload.
- Transpile and import errors carry line and column; dev worker restarts keep storage.

* feat(plugins): orca plugin dev watch

One command for the inner loop: refreshes .orca/types, dev-loads the folder, follows its frame
and worker logs (plugins.getLogs gains an optional cursor), prints reloads (optional
devChangeCount on list entries) and status changes, and unloads on Ctrl-C. --json streams
NDJSON events for agents.

* feat(skills): orca-plugin-authoring bundled skill guide

A guide for agents to build Orca plugins end to end (decision table, build-less rules, the
dev watch loop, test/check, validate, release-entry, approvals, gotchas) with references
generated from the component catalog, host API table, event schemas, manifest schema and CLI
specs, checked for drift. Fixes the components template's Reset to set(count, 0).

* feat(plugins): orca plugin dev watch --check; test kit details in the authoring skill

- --check runs orca plugin check after the load and after each reload (one at a time; saves
  made meanwhile fold into one rerun) and prints each panel's render or errors inline, as a
  `check` event with --json. Local folders only, since it runs the plugin's code here.
- The orca-plugin-authoring guide's Test and check section now documents orca plugin test and
  check from #36969 exactly (JSON results, exit codes, test placement, the orca/testing kit and
  its common mistakes), and the loop uses dev watch --check.

* refactor(plugins): move the components renderer's timing and helper constants to their own module
2026-11-01 03:32:25 -08:00

124 lines
2.6 KiB
TypeScript

export const CLI_GLOBAL_VALUE_FLAGS: readonly string[] = ['pairing-code', 'environment']
export const CLI_GLOBAL_FLAGS: readonly string[] = ['help', 'json', ...CLI_GLOBAL_VALUE_FLAGS]
export const CLI_BOOLEAN_FLAGS = new Set([
'accept-changed-state',
'agent-wait',
'all',
'allow-failed-archive-hook',
'annotate',
'attachments',
'check',
'children',
'comments',
'compact',
'connect',
'current',
'current-json',
'current-sqlite',
'debug',
'delete-branch',
'delta',
'dry-run',
'enter',
'focus',
'grant-desktop-control',
'force',
'fresh',
'full',
'full-tree',
'help',
'if-changed',
'inject',
'include-archived',
'include-remote',
'include-visual-layouts',
'index-status',
'interactive',
'interrupt',
'keep-open',
'json',
'latest-json',
'local',
'messages',
'me',
'mobile',
'mobile-pairing',
'no-pairing',
'screen',
'parent-current',
'provision',
'ready',
'recipe-json',
'reclaim-stale-lock',
'references',
'relations',
'removed',
'reinstall',
'restore-window',
'return-preamble',
'run-hooks',
'show-profile',
'staged',
'supervise',
'tab',
'takeover-legacy',
'tasks',
'text-stdin',
'unread',
'urls',
'value-stdin',
'wait',
'yes'
])
function commandPathStartsAt(
argv: readonly string[],
tokenIndex: number,
path: readonly string[]
): boolean {
let cursor = tokenIndex
for (const part of path) {
while (argv[cursor]?.startsWith('--')) {
const assignment = argv[cursor].slice(2)
const flag = assignment.split('=', 1)[0]
cursor += assignment.includes('=') || CLI_BOOLEAN_FLAGS.has(flag) ? 1 : 2
}
if (argv[cursor] !== part) {
return false
}
cursor += 1
}
return true
}
export function findCliCommandIndex(
argv: readonly string[],
commandPaths: readonly (readonly string[])[],
knownValueFlags: readonly string[] = []
): number {
const startsCommandAt = (index: number): boolean =>
commandPaths.some((path) => commandPathStartsAt(argv, index, path))
for (let index = 0; index < argv.length;) {
const token = argv[index]
if (!token.startsWith('--')) {
return startsCommandAt(index) ? index : -1
}
const assignment = token.slice(2)
const flag = assignment.split('=', 1)[0]
const next = argv[index + 1]
const takesNext =
!assignment.includes('=') &&
!CLI_BOOLEAN_FLAGS.has(flag) &&
next !== undefined &&
!next.startsWith('--') &&
(knownValueFlags.includes(flag) ||
!(startsCommandAt(index + 1) && !startsCommandAt(index + 2)))
index += takesNext ? 2 : 1
}
return -1
}