Open a link or a file in its own window, straight from the prompt.

Open a link or a file in its own app, in a window of its own, straight from the prompt. A link in a terminal is often not clickable, so Pop opens it for you instead of leaving you to copy it out.
/pop example.com opens a web page. A bare domain gets https in front./pop C:/notes/plan.md opens a file in the app your system uses for it.It uses your system's own opener: Start-Process on Windows, open on macOS, and xdg-open on Linux. The address goes to that program as an environment value, never pasted into a command line. Nothing is sent anywhere else.
Nothing goes to any service from the mod itself. Here is each thing, plainly.
What it sends, and where.
/pop goes to your system's own opener program, on this machine. That is the only place the mod sends it.$.process.run, Claude Code's call for starting a program on your machine. Pop uses it only to start the opener below.Which programs it runs, and why.
powershell.exe -NoProfile -Command "Start-Process -FilePath $env:POP_TARGET"open <your link or path>xdg-open <your link or path>POP_TARGET, never inside the command./pop.What it puts in the prompts it submits.
What each hook does.
session.start: adds the /pop command.command.run for pop: puts https in front of a bare domain, runs the opener, and says whether it opened.Tested with Claude Code 2.1.287 on Windows.
See every Rostech mod, live, at rostech.app.
hooks/register.ts 43 lines1import type { EngineInterface, Register } from 'claude-code'
2
3// Each system's own "open this" program, tried in order; the first that runs wins.
4function openers(target: string): readonly (readonly string[])[] {
5 return [
6 ['powershell.exe', '-NoProfile', '-Command', 'Start-Process -FilePath $env:POP_TARGET'],
7 ['open', target],
8 ['xdg-open', target],
9 ]
10}
11
12async function open($: EngineInterface, target: string): Promise<boolean> {
13 for (const argv of openers(target)) {
14 const ran = await $.process
15 .run(argv, { env: { POP_TARGET: target }, timeoutMs: 8000 })
16 .catch(() => null)
17 if (ran && ran.exitCode === 0) return true
18 }
19 return false
20}
21
22// A bare domain gets https in front, so "/pop example.com" opens a page and not a file.
23function target(arg: string): string {
24 const t = arg.trim().replace(/^"|"$/g, '')
25 if (/^[a-z][a-z0-9+.-]*:\/\//i.test(t)) return t
26 if (/^[\w-]+(\.[\w-]+)+(\/\S*)?$/.test(t) && !/\.(md|txt|html?|png|jpe?g|pdf|json)$/i.test(t)) return `https://${t}`
27 return t
28}
29
30export const register: Register = on => {
31 on('session.start', async ($, e, next) => {
32 await $.command.register({ name: 'pop', description: 'Open a link or a file in its own app, in a window of its own. /pop <url or path>' })
33 return next(e)
34 })
35
36 on('command.run', { command: 'pop' }, async ($, e) => {
37 const t = target(e.args)
38 if (!t) return { text: 'Nothing to open. /pop <url or path> opens it in a window of its own.' }
39 const ok = await open($, t)
40 return { text: ok ? `🌐 Opened: ${t}` : `Could not open ${t} on this machine.` }
41 })
42}
43