@0x26/amp 0.6.4 → 0.6.5

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/dist/index.js CHANGED
@@ -1,8 +1,8 @@
1
1
  #!/usr/bin/env node
2
- import{Help}from"commander";import{homedir}from"node:os";import{dirname,join}from"node:path";import{chmodSync,existsSync,mkdirSync,readFileSync,writeFileSync}from"node:fs";import{homedir as homedir2}from"node:os";import{dirname as dirname2,join as join2}from"node:path";import{chmodSync as chmodSync2,existsSync as existsSync2,mkdirSync as mkdirSync2,readFileSync as readFileSync2,writeFileSync as writeFileSync2}from"node:fs";import{closeSync,openSync,rmSync,statSync,unlinkSync}from"node:fs";import{createHash,randomBytes}from"node:crypto";import{closeSync as closeSync2,readSync}from"node:fs";import{Command,CommanderError}from"commander";import{existsSync as existsSync7,rmSync as rmSync3}from"node:fs";import{createServer}from"node:http";import{spawn}from"node:child_process";import"node:fs";import"node:os";import"node:path";import{createHash as createHash2,randomUUID}from"node:crypto";import{mkdirSync as mkdirSync3,readFileSync as readFileSync3,renameSync,writeFileSync as writeFileSync3,rmSync as rmSync2}from"node:fs";import{dirname as dirname3,join as join3}from"node:path";import{homedir as homedir3}from"node:os";import{existsSync as existsSync5,mkdirSync as mkdirSync6,readFileSync as readFileSync6,writeFileSync as writeFileSync6}from"node:fs";import{homedir as homedir5}from"node:os";import{dirname as dirname6,join as join6,resolve as resolve2}from"node:path";import{fileURLToPath}from"node:url";import{Option}from"commander";import{spawnSync}from"node:child_process";import{existsSync as existsSync4,mkdirSync as mkdirSync5,readFileSync as readFileSync5,renameSync as renameSync2,writeFileSync as writeFileSync5}from"node:fs";import{homedir as homedir4}from"node:os";import{dirname as dirname5,join as join5,resolve}from"node:path";import{execFileSync}from"node:child_process";import{existsSync as existsSync3,mkdirSync as mkdirSync4,readFileSync as readFileSync4,writeFileSync as writeFileSync4}from"node:fs";import{dirname as dirname4,join as join4}from"node:path";import{writeFileSync as writeFileSync7}from"node:fs";import{spawn as spawn2}from"node:child_process";import{randomUUID as randomUUID2}from"node:crypto";import{setTimeout as delay}from"node:timers/promises";import{writeFileSync as writeFileSync8}from"node:fs";import{join as join8,resolve as resolve3}from"node:path";var __defProp=Object.defineProperty,__getOwnPropNames=Object.getOwnPropertyNames,__esm=(fn,res)=>function(){return fn&&(res=(0,fn[__getOwnPropNames(fn)[0]])(fn=0)),res},__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})};function colorEnabled(env=process.env,tty=!!process.stdout.isTTY){return tty&&env.TERM!=="dumb"&&env.NO_COLOR===void 0&&env.FORCE_COLOR!=="0"}function paint(text,role="accent",enabled=colorEnabled()){return enabled?`\x1B[${{accent:"38;5;75",muted:"2",strong:"1",error:"31"}[role]}m${text}\x1B[0m`:text}function wrap(text,width){let lines=[],line="";for(let word of clean(text).split(/\s+/)){line&&line.length+word.length+1>width&&(lines.push(line),line="");let rest=word;for(;rest.length>width;)line&&(lines.push(line),line=""),lines.push(rest.slice(0,width)),rest=rest.slice(width);line+=(line?" ":"")+rest}return(line||!lines.length)&&lines.push(line),lines}function brandedHelp(cmd,helper,style,options){let width=Math.max(32,Math.min(options.width,100)),inner=width-4,ink=(s,role="accent")=>paint(s,role,options.color),line=(s="")=>`${ink("\u2502","muted")} ${s} ${ink("\u2502","muted")}`,padded=(s,role)=>line((role?ink(s,role):s)+" ".repeat(Math.max(0,inner-s.length))),panel=(title,rows)=>{let label=` ${title} `,out2=[ink("\u256D\u2500","muted")+ink(label,"strong")+ink("\u2500".repeat(Math.max(0,width-label.length-3))+"\u256E","muted")],termWidth=Math.min(Math.max(0,...rows.map(([term])=>term.length)),Math.floor(inner*.4));for(let[term,description]of rows){if(!description){out2.push(...wrap(term,inner).map(s=>padded(s,"accent")));continue}inner<48||term.length>termWidth?(out2.push(...wrap(term,inner).map(s=>padded(s,"accent"))),out2.push(...wrap(description,inner-2).map(s=>padded(" "+s)))):wrap(description,inner-termWidth-2).forEach((s,i)=>{let left=i===0?term:"";out2.push(line(ink(left)+" ".repeat(termWidth-left.length+2)+s+" ".repeat(inner-termWidth-2-s.length)))})}return out2.push(ink("\u2570"+"\u2500".repeat(width-2)+"\u256F","muted")),out2.join(`
2
+ import{Help}from"commander";import{homedir}from"node:os";import{dirname,join}from"node:path";import{chmodSync,existsSync,mkdirSync,readFileSync,writeFileSync}from"node:fs";import{homedir as homedir2}from"node:os";import{dirname as dirname2,join as join2}from"node:path";import{chmodSync as chmodSync2,existsSync as existsSync2,mkdirSync as mkdirSync2,readFileSync as readFileSync2,writeFileSync as writeFileSync2}from"node:fs";import{closeSync,openSync,rmSync,statSync,unlinkSync}from"node:fs";import{createHash,randomBytes}from"node:crypto";import{closeSync as closeSync2,readSync}from"node:fs";import{Command,CommanderError}from"commander";import{existsSync as existsSync7,rmSync as rmSync3}from"node:fs";import{createServer}from"node:http";import{spawn}from"node:child_process";import"node:fs";import"node:os";import"node:path";import{createHash as createHash2,randomUUID}from"node:crypto";import{mkdirSync as mkdirSync3,readFileSync as readFileSync3,renameSync,writeFileSync as writeFileSync3,rmSync as rmSync2}from"node:fs";import{dirname as dirname3,join as join3}from"node:path";import{homedir as homedir3}from"node:os";import{existsSync as existsSync5,mkdirSync as mkdirSync6,readFileSync as readFileSync6,renameSync as renameSync3,writeFileSync as writeFileSync6}from"node:fs";import{homedir as homedir5}from"node:os";import{dirname as dirname6,join as join6,resolve as resolve2}from"node:path";import{fileURLToPath}from"node:url";import{Option}from"commander";import{spawnSync}from"node:child_process";import{existsSync as existsSync4,mkdirSync as mkdirSync5,readFileSync as readFileSync5,renameSync as renameSync2,writeFileSync as writeFileSync5}from"node:fs";import{homedir as homedir4}from"node:os";import{dirname as dirname5,join as join5,resolve}from"node:path";import{execFileSync}from"node:child_process";import{existsSync as existsSync3,mkdirSync as mkdirSync4,readFileSync as readFileSync4,writeFileSync as writeFileSync4}from"node:fs";import{dirname as dirname4,join as join4}from"node:path";import{writeFileSync as writeFileSync7}from"node:fs";import{spawn as spawn2}from"node:child_process";import{randomUUID as randomUUID2}from"node:crypto";import{setTimeout as delay}from"node:timers/promises";import{writeFileSync as writeFileSync8}from"node:fs";import{join as join8,resolve as resolve3}from"node:path";var __defProp=Object.defineProperty,__getOwnPropNames=Object.getOwnPropertyNames,__esm=(fn,res)=>function(){return fn&&(res=(0,fn[__getOwnPropNames(fn)[0]])(fn=0)),res},__export=(target,all)=>{for(var name in all)__defProp(target,name,{get:all[name],enumerable:!0})};function colorEnabled(env=process.env,tty=!!process.stdout.isTTY){return tty&&env.TERM!=="dumb"&&env.NO_COLOR===void 0&&env.FORCE_COLOR!=="0"}function paint(text,role="accent",enabled=colorEnabled()){return enabled?`\x1B[${{accent:"38;5;75",muted:"2",strong:"1",error:"31"}[role]}m${text}\x1B[0m`:text}function wrap(text,width){let lines=[],line="";for(let word of clean(text).split(/\s+/)){line&&line.length+word.length+1>width&&(lines.push(line),line="");let rest=word;for(;rest.length>width;)line&&(lines.push(line),line=""),lines.push(rest.slice(0,width)),rest=rest.slice(width);line+=(line?" ":"")+rest}return(line||!lines.length)&&lines.push(line),lines}function brandedHelp(cmd,helper,style,options){let width=Math.max(32,Math.min(options.width,100)),inner=width-4,ink=(s,role="accent")=>paint(s,role,options.color),line=(s="")=>`${ink("\u2502","muted")} ${s} ${ink("\u2502","muted")}`,padded=(s,role)=>line((role?ink(s,role):s)+" ".repeat(Math.max(0,inner-s.length))),panel=(title,rows)=>{let label=` ${title} `,out2=[ink("\u256D\u2500","muted")+ink(label,"strong")+ink("\u2500".repeat(Math.max(0,width-label.length-3))+"\u256E","muted")],termWidth=Math.min(Math.max(0,...rows.map(([term])=>term.length)),Math.floor(inner*.4));for(let[term,description]of rows){if(!description){out2.push(...wrap(term,inner).map(s=>padded(s,"accent")));continue}inner<48||term.length>termWidth?(out2.push(...wrap(term,inner).map(s=>padded(s,"accent"))),out2.push(...wrap(description,inner-2).map(s=>padded(" "+s)))):wrap(description,inner-termWidth-2).forEach((s,i)=>{let left=i===0?term:"";out2.push(line(ink(left)+" ".repeat(termWidth-left.length+2)+s+" ".repeat(inner-termWidth-2-s.length)))})}return out2.push(ink("\u2570"+"\u2500".repeat(width-2)+"\u256F","muted")),out2.join(`
3
3
  `)},root=!cmd.parent,out=[""];if(root&&width>=55){let mark=[" \u2584\u2580\u2580\u2584 "," \u2580\u2584\u2584\u2580 "," \u2584\u2580 \u2580\u2584\u2580 "," \u2580\u2584\u2584\u2584\u2580 \u2580 "],text=["AMPERSAND",`${cmd.name()} / ${style.edition}`,cmd.version()?`v${cmd.version()}`:"",""];mark.forEach((s,i)=>out.push(" "+ink(s)+" "+ink(text[i],i===0?"strong":"muted")))}else out.push(ink(` & AMPERSAND / ${cmd.name()}`,"strong"));out.push("",...wrap(helper.commandDescription(cmd),width).map(s=>ink(s,"muted")),""),out.push(panel("Usage",[[helper.commandUsage(cmd),""]]));let commands=helper.visibleCommands(cmd);if(commands.length){let used=new Set;for(let group of root?style.groups??[]:[]){let members=commands.filter(c=>group.commands.includes(c.name()));members.length&&(members.forEach(c=>used.add(c.name())),out.push("",panel(group.title,members.map(c=>[helper.subcommandTerm(c),helper.subcommandDescription(c)]))))}let remaining=commands.filter(c=>!used.has(c.name()));remaining.length&&out.push("",panel(used.size?"More commands":"Commands",remaining.map(c=>[helper.subcommandTerm(c),helper.subcommandDescription(c)])))}let args=helper.visibleArguments(cmd);args.length&&out.push("",panel("Arguments",args.map(a=>[helper.argumentTerm(a),helper.argumentDescription(a)])));let opts=helper.visibleOptions(cmd);return opts.length&&out.push("",panel("Options",opts.map(o=>[helper.optionTerm(o),helper.optionDescription(o)]))),root?out.push("",panel("Start here",style.examples.map(e=>[e,""]))):out.push("",ink("Global options: "+(()=>{let root2=cmd;for(;root2.parent;)root2=root2.parent;return root2.name()})()+" --help","muted")),out.push(""),out.join(`
4
4
  `)}function installTerminalHelp(program2,style){program2.configureHelp({formatHelp(cmd,helper){let args=process.argv.slice(2);return!process.stdout.isTTY||process.env.TERM==="dumb"||args.includes("--agent")||args.includes("--json")?Help.prototype.formatHelp.call(helper,cmd,helper):brandedHelp(cmd,helper,style,{width:process.stdout.columns||80,color:colorEnabled()})}})}function askProgress(write2,options={}){let now=options.now??Date.now,start=now(),frames=["\u280B","\u2819","\u2839","\u2838","\u283C","\u2834","\u2826","\u2827","\u2807","\u280F"],stage="Connecting",frame=0,stopped=!1,lastUpdate=start,draw=()=>{if(stopped)return;let elapsed=`${Math.floor((now()-start)/1e3)}s`,width=Math.max(12,(options.width??process.stderr.columns??80)-1),stale=Math.floor((now()-lastUpdate)/1e3),age=stale>=15?` \xB7 last update ${stale}s ago`:"",suffix=` \xB7 ${elapsed}${age.length+elapsed.length+13<=width?age:""}`,text=stage.slice(0,Math.max(1,width-suffix.length-2));write2(`\r\x1B[2K${paint(frames[frame++%frames.length],"accent",options.color)} ${text}${suffix}`)};draw();let timer=setInterval(draw,100);return timer.unref(),{update(message){stage=clean(message).trim().replace(/\s+/g," ")||"Working",lastUpdate=now()},note(message){stopped||(write2("\r\x1B[2K"+clean(message)+`
5
- `),draw())},stop(){stopped||(stopped=!0,clearInterval(timer),write2("\r\x1B[2K"))}}}var clean,init_terminal=__esm({"src/terminal.ts"(){"use strict";clean=text=>text.replace(/[\x00-\x1f\x7f-\x9f]/g," ")}}),CLI_VERSION,init_version=__esm({"src/version.ts"(){"use strict";CLI_VERSION="0.6.4"}}),NPM_PACKAGE,init_brand=__esm({"src/brand.ts"(){"use strict";NPM_PACKAGE="@0x26/amp"}});function configPath(){return process.env.AMP_CONFIG_PATH??join(homedir(),".config","amp","config.json")}function readConfig(){let p=configPath();if(!existsSync(p))return{profiles:{}};try{let parsed=JSON.parse(readFileSync(p,"utf8"));return{current:parsed.current,profiles:parsed.profiles??{}}}catch{return{profiles:{}}}}function writeConfig(cfg){let p=configPath();mkdirSync(dirname(p),{recursive:!0,mode:448}),writeFileSync(p,`${JSON.stringify(cfg,null,2)}
5
+ `),draw())},stop(){stopped||(stopped=!0,clearInterval(timer),write2("\r\x1B[2K"))}}}var clean,init_terminal=__esm({"src/terminal.ts"(){"use strict";clean=text=>text.replace(/[\x00-\x1f\x7f-\x9f]/g," ")}}),CLI_VERSION,init_version=__esm({"src/version.ts"(){"use strict";CLI_VERSION="0.6.5"}}),NPM_PACKAGE,init_brand=__esm({"src/brand.ts"(){"use strict";NPM_PACKAGE="@0x26/amp"}});function configPath(){return process.env.AMP_CONFIG_PATH??join(homedir(),".config","amp","config.json")}function readConfig(){let p=configPath();if(!existsSync(p))return{profiles:{}};try{let parsed=JSON.parse(readFileSync(p,"utf8"));return{current:parsed.current,profiles:parsed.profiles??{}}}catch{return{profiles:{}}}}function writeConfig(cfg){let p=configPath();mkdirSync(dirname(p),{recursive:!0,mode:448}),writeFileSync(p,`${JSON.stringify(cfg,null,2)}
6
6
  `,{mode:384}),chmodSync(p,384)}function setProfileOverride(name){profileOverride=name}function activeProfile(){let cfg=readConfig(),name=profileOverride??process.env.AMP_PROFILE??cfg.current;if(!name)return null;let rec=cfg.profiles[name];if(!rec)throw new Error(`unknown profile '${name}' \u2014 \`amp profiles\` lists them; \`amp login --store <url> --name ${name}\` creates it`);return{name,store_url:rec.store_url}}function activeProfileQuiet(){try{return activeProfile()}catch{return null}}function profileCredentialsPath(name){if(!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(name)||name==="."||name==="..")throw new Error("Invalid profile name.");return join(dirname(configPath()),"profiles",name,"credentials")}function normalizeStoreUrl(input){let url=input.trim().replace(/\/+$/,"");url=url.replace(/\/mcp$/,""),/^https?:\/\//.test(url)||(url=`https://${url}`);let parsed=new URL(url);if(parsed.username||parsed.password||parsed.search||parsed.hash||parsed.pathname!=="/"||parsed.protocol!=="https:"&&!(parsed.protocol==="http:"&&["localhost","127.0.0.1","[::1]"].includes(parsed.hostname)))throw new Error("Store must be an HTTPS origin (HTTP is allowed only on loopback).");return parsed.origin}function profileNameFromStore(storeUrl){return new URL(storeUrl).hostname}var profileOverride,init_config=__esm({"src/config.ts"(){"use strict";init_brand()}});function credentialsPath(){if(process.env.AMP_CREDENTIALS_PATH)return process.env.AMP_CREDENTIALS_PATH;let profile=activeProfile();return profile?profileCredentialsPath(profile.name):join2(homedir2(),".config","amp","credentials")}function readRaw(){let path=credentialsPath();if(!existsSync2(path))return null;let parsed;try{parsed=JSON.parse(readFileSync2(path,"utf8"))}catch{return null}return!parsed.jwt||!parsed.expires_at?null:parsed}function write(creds){let path=credentialsPath();mkdirSync2(dirname2(path),{recursive:!0,mode:448}),writeFileSync2(path,`${JSON.stringify(creds,null,2)}
7
7
  `,{mode:384}),chmodSync2(path,384)}var init_credentials=__esm({"src/credentials.ts"(){"use strict";init_brand(),init_config()}});async function withFileLock(lockPath,fn){let deadline=Date.now()+MAX_WAIT_MS,fd;for(;;)try{fd=openSync(lockPath,"wx");break}catch(err){if(err?.code!=="EEXIST")throw err;try{if(Date.now()-statSync(lockPath).mtimeMs>STALE_MS){rmSync(lockPath,{force:!0});continue}}catch{}if(Date.now()>deadline)try{rmSync(lockPath,{force:!0}),fd=openSync(lockPath,"wx");break}catch{return fn()}await sleep(POLL_MS)}try{return await fn()}finally{fd!==void 0&&closeSync(fd);try{unlinkSync(lockPath)}catch{}}}var STALE_MS,POLL_MS,MAX_WAIT_MS,sleep,init_lock=__esm({"src/lock.ts"(){"use strict";STALE_MS=15e3,POLL_MS=50,MAX_WAIT_MS=2e4,sleep=ms=>new Promise(r=>setTimeout(r,ms))}});function resolveExperience(opts,stdoutTTY,stdinTTY){if(opts.agent&&opts.human||opts.human&&opts.json||opts.agent&&opts.json)throw new CliError("INVALID_ARGUMENT","Choose one of --agent, --human, or --json.",!1);let agent=!!opts.agent||!opts.human&&!opts.json&&!stdoutTTY;return{enabled:!0,agent,json:!!opts.json,quiet:!!opts.quiet||agent,noInput:opts.input===!1||agent||!stdinTTY}}function configureExperience(opts){current=resolveExperience(opts,!!process.stdout.isTTY,!!process.stdin.isTTY)}function progress(text){current.quiet||process.stderr.write(text)}function envelope(data,meta={}){return{data,meta:{version:1,...meta}}}function httpError(status){return status===401?new CliError("AUTH_REQUIRED","Sign in to your workspace again.",!1,"amp login"):status===403?new CliError("ACCESS_DENIED","This session cannot access the requested workspace or data. Check the selected profile and ask your workspace administrator for access.",!1,"amp login --status"):status===404?new CliError("NOT_FOUND","The requested resource or feature is unavailable in this workspace.",!1,"amp schema"):status===429?new CliError("RATE_LIMITED","Too many requests. Wait before retrying.",!0):status>=500?new CliError("SERVICE_UNAVAILABLE","The service is temporarily unavailable. Try again later.",!0):new CliError("REQUEST_REJECTED","The request was rejected. Check its arguments and your workspace permissions.",!1)}function errorPayload(error){let e=error instanceof CliError?error:new CliError("COMMAND_FAILED","The command could not complete. Check your connection and command arguments.");return{error:{code:e.code,message:e.message,retryable:e.retryable,...e.nextCommand?{next_command:e.nextCommand}:{},...e.details?{details:e.details}:{}}}}function renderError(error){let payload=errorPayload(error);current.agent||current.json?process.stdout.write(`${JSON.stringify(payload)}
8
8
  `):process.stderr.write(`${paint("\xD7","error",colorEnabled(process.env,!!process.stderr.isTTY))} ${payload.error.message}
@@ -49,8 +49,9 @@ try {
49
49
  emit("Amp skill sync or refresh failed. Do not use Ask until the CLI and current published instructions are available.", true);
50
50
  }
51
51
  `}function installCodexSessionHook(home=codexHomePath()){mkdirSync5(home,{recursive:!0,mode:448});let settingsPath=join5(home,"hooks.json"),settings={};if(existsSync4(settingsPath))try{let parsed=JSON.parse(readFileSync5(settingsPath,"utf8"));if(!parsed||typeof parsed!="object"||Array.isArray(parsed))return"skipped";settings=parsed}catch{return"skipped"}let hooks=settings.hooks??{};if(!hooks||typeof hooks!="object"||Array.isArray(hooks))return"skipped";let groups=hooks.SessionStart??[];if(!Array.isArray(groups))return"skipped";let scriptDir=join5(home,"hooks"),scriptPath=join5(scriptDir,"amp-skill-sync.cjs"),command=`node ${shellQuote(scriptPath)}`;mkdirSync5(scriptDir,{recursive:!0,mode:448});let source=codexHookSource(join5(home,"skills","amp-cli")),sourceChanged=!existsSync4(scriptPath)||readFileSync5(scriptPath,"utf8")!==source;if(sourceChanged&&writeFileSync5(scriptPath,source,{mode:448}),groups.some(group=>Array.isArray(group?.hooks)&&group.hooks.some(hook=>typeof hook?.command=="string"&&hook.command===command)))return sourceChanged?"installed":"present";groups.push({matcher:"startup|resume|clear",hooks:[{type:"command",command,statusMessage:"Refreshing Amp CLI skill"}]}),settings.hooks={...hooks,SessionStart:groups};let tmp=`${settingsPath}.${process.pid}.tmp`;return writeFileSync5(tmp,`${JSON.stringify(settings,null,2)}
52
- `),renameSync2(tmp,settingsPath),"installed"}function reexecSync(target){let r=spawnSync("amp",["--agent","skill","sync","--skip-update",...target===defaultInstallPath()?[]:["--dir",dirname5(target)]],{encoding:"utf8",timeout:3e4,shell:process.platform==="win32"});if(r.status!==0)return null;try{return JSON.parse(r.stdout).data}catch{return null}}async function syncSkill(opts={}){let target=opts.target??defaultInstallPath(),lock=join5(dirname5(configPath()),"skill-sync.lock");mkdirSync5(dirname5(lock),{recursive:!0,mode:448});let cli=opts.skipUpdate?{status:"skipped",current:CLI_VERSION}:await withFileLock(lock,async()=>(opts.update??ensureLatestCli)());if(cli.status==="updated"){let child=(opts.reexec??reexecSync)(target);if(child)return{...child,cli}}return withFileLock(lock,async()=>{let result={cli,skill:{path:target,outcome:installSkill(target)}},service=existsSync4(target)?skillService(readFileSync5(target,"utf8")):void 0;if(service){let published=await fetchSkill(service,bundledSkill,{fetch:opts.fetch,cacheDir:opts.cacheDir});result.published={url:published.url,source:published.source,stale:published.stale,version:published.version,...published.warning?{warning:published.warning}:{}}}return result})}var STAMP_PREFIX="<!-- amp-managed skill \xB7 amp ";function bundledSkillPath(){return resolve2(dirname6(fileURLToPath(import.meta.url)),"..","skill","SKILL.md")}function bundledSkill(){let p=bundledSkillPath();if(!existsSync5(p))throw new Error("Bundled skill missing. Reinstall the CLI package.");return readFileSync6(p,"utf8")}function defaultInstallPath(){return join6(homedir5(),".claude","skills","amp-cli","SKILL.md")}function frontmatterEnd(text){return text.match(/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/)?.[0].length??0}function stamped(text){let marker=`${STAMP_PREFIX}${CLI_VERSION} \u2014 bundled customer skill; remove this stamp to preserve manual edits -->
53
- `,end=frontmatterEnd(text);return`${text.slice(0,end)}${marker}${text.slice(end)}`}function isAmpManaged(text){return text.startsWith(STAMP_PREFIX)||text.slice(frontmatterEnd(text)).startsWith(STAMP_PREFIX)}function installSkill(target=defaultInstallPath(),service){let current2=existsSync5(target)?readFileSync6(target,"utf8"):void 0;if(current2!==void 0&&!isAmpManaged(current2))return"kept-hand-edited";let publisher=service??(current2===void 0?void 0:skillService(current2)),next=stamped(publisher?bootstrapSkill(publisher):bundledSkill());return current2!==void 0?(current2===next||writeFileSync6(target,next),"refreshed"):(mkdirSync6(dirname6(target),{recursive:!0}),writeFileSync6(target,next),"installed")}function skillService(text){return text.match(/^<!-- amp-skill-service: (https:\/\/[^\s]+) -->$/m)?.[1]}function bootstrapSkill(service){let url=skillUrl(service),origin2=new URL(url).origin;return`---
52
+ `),renameSync2(tmp,settingsPath),"installed"}function reexecSync(target){let r=spawnSync("amp",["--agent","skill","sync","--skip-update",...target===defaultInstallPath()?[]:["--dir",dirname5(target)]],{encoding:"utf8",timeout:3e4,shell:process.platform==="win32"});if(r.status!==0)return null;try{return JSON.parse(r.stdout).data}catch{return null}}async function syncSkill(opts={}){let target=opts.target??defaultInstallPath(),lock=join5(dirname5(configPath()),"skill-sync.lock");mkdirSync5(dirname5(lock),{recursive:!0,mode:448});let cli=opts.skipUpdate?{status:"skipped",current:CLI_VERSION}:await withFileLock(lock,async()=>(opts.update??ensureLatestCli)());if(cli.status==="updated"){let child=(opts.reexec??reexecSync)(target);if(child)return{...child,cli}}return withFileLock(lock,async()=>{let result={cli,skill:{path:target,outcome:installSkill(target)}},service=installedSkillService(target);if(service){let published=await fetchSkill(service,bundledSkill,{fetch:opts.fetch,cacheDir:opts.cacheDir});result.published={url:published.url,source:published.source,stale:published.stale,version:published.version,...published.warning?{warning:published.warning}:{}}}return result})}var STAMP_PREFIX="<!-- amp-managed skill \xB7 amp ";function bundledSkillPath(){return resolve2(dirname6(fileURLToPath(import.meta.url)),"..","skill","SKILL.md")}function bundledSkill(){let p=bundledSkillPath();if(!existsSync5(p))throw new Error("Bundled skill missing. Reinstall the CLI package.");return readFileSync6(p,"utf8")}function defaultInstallPath(){return join6(homedir5(),".claude","skills","amp-cli","SKILL.md")}function frontmatterEnd(text){return text.match(/^---\r?\n[\s\S]*?\r?\n---(?:\r?\n|$)/)?.[0].length??0}function stamped(text){let marker=`${STAMP_PREFIX}${CLI_VERSION} \u2014 bundled customer skill; remove this stamp to preserve manual edits -->
53
+ `,end=frontmatterEnd(text);return`${text.slice(0,end)}${marker}${text.slice(end)}`}function isAmpManaged(text){return text.startsWith(STAMP_PREFIX)||text.slice(frontmatterEnd(text)).startsWith(STAMP_PREFIX)}function installSkill(target=defaultInstallPath(),service){if(service!==void 0){let origin2=new URL(skillUrl(service)).origin;mkdirSync6(dirname6(target),{recursive:!0});let temporary=`${target}.publisher.json.${process.pid}.tmp`;writeFileSync6(temporary,`${JSON.stringify({service:origin2})}
54
+ `,{mode:384}),renameSync3(temporary,`${target}.publisher.json`)}let current2=existsSync5(target)?readFileSync6(target,"utf8"):void 0;if(current2!==void 0&&!isAmpManaged(current2))return"kept-hand-edited";let publisher=service??installedSkillService(target),next=stamped(publisher?bootstrapSkill(publisher):bundledSkill());return current2!==void 0?(current2===next||writeFileSync6(target,next),"refreshed"):(mkdirSync6(dirname6(target),{recursive:!0}),writeFileSync6(target,next),"installed")}function skillService(text){return text.match(/^<!-- amp-skill-service: (https:\/\/[^\s]+) -->$/m)?.[1]}function installedSkillService(target){let metadata=`${target}.publisher.json`;if(existsSync5(metadata)){let value=JSON.parse(readFileSync6(metadata,"utf8"));if(!value||typeof value!="object"||!("service"in value)||typeof value.service!="string")throw new Error("Invalid skill publisher metadata");return new URL(skillUrl(value.service)).origin}return existsSync5(target)?skillService(readFileSync6(target,"utf8")):void 0}function bootstrapSkill(service){let url=skillUrl(service),origin2=new URL(url).origin;return`---
54
55
  name: amp-cli
55
56
  description: Ask evidence-backed questions about workspace activity with Amp, including related follow-ups and corrections without mentioning Amp again.
56
57
  ---
@@ -172,7 +173,7 @@ ${result.warning?result.warning+`
172
173
  ${url}
173
174
  `)}),child.unref()}catch{process.stdout.write(`Open this URL to sign in:
174
175
  ${url}
175
- `)}}async function login(opts={}){if(experience().enabled&&(experience().noInput||experience().json))throw new CliError("INTERACTION_REQUIRED","Browser sign-in needs a person. Run login in an interactive terminal.",!1,"amp login --store <your workspace URL>");let cid=clientId(),profileName,storeUrl;if(opts.store)storeUrl=normalizeStoreUrl(opts.store),profileName=opts.name??profileNameFromStore(storeUrl),profileCredentialsPath(profileName);else if(opts.name)throw new Error("--name names a profile for --store; pass --store <url> with it");let organizationId=opts.orgId??(opts.store?void 0:bundle().defaultOrgId),{verifier,challenge}=generatePkce(),state=generateState(),authorizeUrl=buildAuthorizeUrl({clientId:cid,redirectUri:REDIRECT_URI,codeChallenge:challenge,state,organizationId}),codePromise=startCallbackServer(state);process.stdout.write(`Open this URL to sign in:
176
+ `)}}async function login(opts={}){if(experience().enabled&&(experience().noInput||experience().json))throw new CliError("INTERACTION_REQUIRED","Browser sign-in requires terminal stdin and stdout. In a coding harness, retry login in an interactive PTY (Codex: tty=true) without --agent, --no-input, --json, or a pipe. Keep the process running through browser sign-in, then continue setup.",!1,"amp --human login --store <your workspace URL>");let cid=clientId(),profileName,storeUrl;if(opts.store)storeUrl=normalizeStoreUrl(opts.store),profileName=opts.name??profileNameFromStore(storeUrl),profileCredentialsPath(profileName);else if(opts.name)throw new Error("--name names a profile for --store; pass --store <url> with it");let organizationId=opts.orgId??(opts.store?void 0:bundle().defaultOrgId),{verifier,challenge}=generatePkce(),state=generateState(),authorizeUrl=buildAuthorizeUrl({clientId:cid,redirectUri:REDIRECT_URI,codeChallenge:challenge,state,organizationId}),codePromise=startCallbackServer(state);process.stdout.write(`Open this URL to sign in:
176
177
  ${authorizeUrl}
177
178
  `),openBrowser(authorizeUrl);let code2=await codePromise,result=await exchangeCode({code:code2,codeVerifier:verifier,clientId:cid}),claims=decodeJwtPayload(result.access_token);if(storeUrl&&profileName){let cfg=readConfig();cfg.profiles[profileName]={store_url:storeUrl},cfg.current=profileName,writeConfig(cfg),setProfileOverride(profileName)}if(write({jwt:result.access_token,expires_at:expiryFromClaims(claims),refresh_token:result.refresh_token,email:result.user.email,org_id:result.organization_id??claims.org_id??"",org_name:""}),process.stdout.write(`\u2713 Logged in as ${result.user.email}
178
179
  `),profileName&&storeUrl){process.stdout.write(`\u2713 profile: ${profileName} \u2192 ${storeUrl} (now current)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@0x26/amp",
3
- "version": "0.6.4",
3
+ "version": "0.6.5",
4
4
  "description": "Query your data with Ampersand",
5
5
  "type": "module",
6
6
  "bin": {
package/skill/SKILL.md CHANGED
@@ -32,11 +32,13 @@ scope before retrieving it.
32
32
 
33
33
  These steps apply in any agent environment: Claude Code, Codex, Cursor, a cloud sandbox, or a plain terminal.
34
34
 
35
+ - For setup from a supplied SKILL.md URL, fetch and read that URL now; a preserved local skill is not a substitute. If the web reader cannot fetch it, use `amp --agent skill fetch --service <supplied-origin>` or a direct HTTP fetch. If login returns `INTERACTION_REQUIRED`, retry it yourself with `amp --human login` in an interactive PTY (`tty: true` in Codex). That error does not establish that the harness lacks a terminal. After the user says they signed in, finish skill installation, refresh, login-status and schema checks; then ask the exact setup question below.
36
+
35
37
  - **Before the first Ask request in every new agent session, refresh and reread this skill.** Prefer the harness's `SessionStart` hook. If the hook has not successfully synced this session (including a missing, disabled, or untrusted hook), run `amp --no-input skill sync --dir <this skill's directory>` and reread `SKILL.md` before invoking `ask`. Do not start Ask if sync fails; report the failure. If sync reports `kept-hand-edited`, it preserved the local file: use `published.url` from the sync result to derive the service origin, run `amp --agent skill fetch --service <origin>`, and read the returned `data.text` for this session. If the fetch fails or is stale, tell the user and do not claim the local instructions are current.
36
38
 
37
39
  - If `amp --version` fails because the command is not found, install the CLI before anything else. It needs Node.js 20 or later: `npm install -g @0x26/amp`. If you cannot install it in this environment (no Node.js, network, or permission), tell the user and give them that command; never install from another source.
38
40
  - `amp` uses the sign-in stored on the machine where it runs. If `amp --agent login --status` shows no session, run `amp --human login --store <tenant-store-url>` yourself in an interactive PTY with terminal stdin and stdout. Do not add `--agent`, `--no-input`, or `--json`, or pipe its output. Keep the process active while the user completes browser sign-in, then continue setup in the same chat. Hand off a terminal command only if the harness cannot support browser login on that machine. A sign-in on the user's laptop does not reach a remote sandbox, and the reverse. Never request or print tokens.
39
- - Install the persistent skill and hook yourself: `amp skill install` for Claude Code or `amp skill install --harness codex` for Codex. Codex requires the user to review and trust its SessionStart hook before the hook runs. A pending hook does not block setup or Ask: sync and reread the skill manually for this session; do not run an untrusted hook or claim automatic sync is active. Add `--service <published-origin>` when supplied; a SKILL.md URL supplied for setup also gives its publisher origin. Enabled, trusted hooks sync the CLI and skill at session start. Other harnesses without a hook must run the sync command before Ask. Reread the installed skill and continue in the current chat; do not require a new session to finish setup.
41
+ - Install the skill and hook in the harness’s **user-wide directory**, so Amp is available across projects and future sessions. Run `amp skill install` for Claude Code (`~/.claude/skills/amp-cli/SKILL.md`) or `amp skill install --harness codex` for Codex (`$CODEX_HOME/skills/amp-cli/SKILL.md`, or `~/.codex/skills/amp-cli/SKILL.md` when `CODEX_HOME` is unset). Keep the configured `CODEX_HOME`. Use these defaults without `--dir` so the session hook is also installed. For another harness, use `--dir <user-wide-skill-directory>` and arrange a session-start sync. Check the returned install path; do not save only to the current project or merely read the file for this chat. Codex requires the user to review and trust its SessionStart hook before the hook runs. A pending hook does not block setup or Ask: sync and reread the skill manually for this session; do not run an untrusted hook or claim automatic sync is active. Add `--service <published-origin>` when supplied; a SKILL.md URL supplied for setup also gives its publisher origin. Enabled, trusted hooks sync the CLI and skill at session start. Other harnesses without a hook must run the sync command before Ask. Reread the installed skill and continue in the current chat; do not require a new session to finish setup.
40
42
  - After setup, verify login status for the requested store and run `amp --agent schema` without displaying routine output. Do not stop after sign-in or report installation, identity, profile, or schema details. For setup without an Amp question, once both checks succeed, reply exactly: "What would you like to ask amp?" If a question was already supplied, continue that question after setup instead.
41
43
 
42
44
  ## Use