dunsocial 0.5.0 → 0.7.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/README.md CHANGED
@@ -82,6 +82,8 @@ dun auth status
82
82
  dun posts list
83
83
  dun media upload ./photo.png
84
84
  dun drafts create --text "Idea for later"
85
+ dun personalization get
86
+ dun personalization set --description "Concise, direct, no fluff."
85
87
  ```
86
88
 
87
89
  Tips:
@@ -89,6 +91,7 @@ Tips:
89
91
  - Help for any command: `dun posts --help`
90
92
  - Structured output for scripts: add `--json`
91
93
  - Prefer ChatGPT or Claude **in the browser**? Use [MCP](https://docs.dunsocial.com/mcp) instead of the CLI
94
+ - Personalization is workspace-wide AI voice (Settings → Personalization). Updating it needs a token with the `workspace:write` scope
92
95
 
93
96
  ---
94
97
 
package/dist/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  #!/usr/bin/env bun
2
2
  // @bun
3
- import{mkdir as L0,readFile as EU,writeFile as k0,chmod as M0,unlink as j0}from"node:fs/promises";import{dirname as d0,join as KU}from"node:path";import{homedir as CU}from"node:os";var z={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class Q extends Error{code;details;constructor(U,D=z.USAGE,O){super(U);this.name="CliError",this.code=D,this.details=O}}function $U(U){if(U===401)return z.AUTH;if(U===403)return z.FORBIDDEN;if(U===404)return z.NOT_FOUND;if(U===429)return z.RATE_LIMITED;if(U>=400&&U<500)return z.VALIDATION;return z.NETWORK}var pU="https://api.dunsocial.com",v="0.4.0";function nU(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let U=process.env.XDG_CONFIG_HOME,D=U?U:KU(CU(),".config");return KU(D,"dunsocial","config.json")}async function fU(){let U=nU();try{let D=await EU(U,"utf8");return JSON.parse(D)}catch(D){if(D.code==="ENOENT")return{};throw new Q(`Failed to read config at ${U}: ${D.message}`,z.USAGE)}}async function VU(U){let D=await fU(),O=(U.apiUrl||process.env.DUN_API_URL||D.apiUrl||pU).replace(/\/$/,""),K=U.token||process.env.DUN_TOKEN||D.token,G=U.workspace||process.env.DUN_WORKSPACE_ID||D.defaultWorkspaceId||void 0,$=process.env.DUN_JSON==="0",A=!process.stdout.isTTY,V=$?!1:U.json===!0||process.env.DUN_JSON==="1"||process.env.CI==="true"||process.env.CI==="1"||A;return{apiUrl:O,token:K,workspaceId:G,json:V,debug:U.debug===!0||process.env.DUN_DEBUG==="1",quiet:U.quiet===!0,yes:U.yes===!0||process.env.CI==="true"||process.env.CI==="1",config:D}}function AU(U){let D={},O=[],K=0,G=!0;while(K<U.length){let Z=U[K];if(G&&Z==="--"){G=!1,K+=1;continue}if(G&&Z.startsWith("--")){let J=Z.indexOf("=");if(J!==-1){D[Z.slice(2,J)]=Z.slice(J+1),K+=1;continue}let N=Z.slice(2),q=U[K+1];if(q!==void 0&&!q.startsWith("-"))D[N]=q,K+=2;else D[N]=!0,K+=1;continue}if(G&&Z.startsWith("-")&&Z.length>1&&Z!=="-"){let J=Z.slice(1);if(J.length>1&&!J.includes("=")){for(let I of J)D[I]=!0;K+=1;continue}let N=J,q=U[K+1];if(q!==void 0&&!q.startsWith("-"))D[N]=q,K+=2;else D[N]=!0,K+=1;continue}O.push(Z),K+=1}let $=O[0],A,V,X=1;if($==="memory"&&(O[1]==="collections"||O[1]==="collection"))V="collections",A=O[2],X=A?3:2;else A=O[1],X=A?2:1;return{group:$,action:A,nest:V,flags:D,positionals:O.slice(X)}}function m(U,...D){for(let O of D){let K=U[O];if(typeof K==="string"&&K.length>0)return K}return}function y(U,...D){for(let O of D){let K=U[O];if(K===!0)return!0;if(typeof K==="string"){let G=K.toLowerCase();if(G==="1"||G==="true"||G==="yes")return!0}}return!1}function n(U){return y(U,"help","h")}function f(U){return y(U,"version","v","V")}function x(U,D){if(D.quiet&&!D.json)return;if(D.json||!process.stdout.isTTY){process.stdout.write(`${JSON.stringify({ok:!0,data:U},null,2)}
3
+ import{mkdir as M0,readFile as CU,writeFile as j0,chmod as b0,unlink as S0}from"node:fs/promises";import{dirname as h0,join as AU}from"node:path";import{homedir as pU}from"node:os";var z={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class Q extends Error{code;details;constructor(U,D=z.USAGE,O){super(U);this.name="CliError",this.code=D,this.details=O}}function $U(U){if(U===401)return z.AUTH;if(U===403)return z.FORBIDDEN;if(U===404)return z.NOT_FOUND;if(U===429)return z.RATE_LIMITED;if(U>=400&&U<500)return z.VALIDATION;return z.NETWORK}var nU="https://api.dunsocial.com",v="0.4.0";function fU(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let U=process.env.XDG_CONFIG_HOME,D=U?U:AU(pU(),".config");return AU(D,"dunsocial","config.json")}async function iU(){let U=fU();try{let D=await CU(U,"utf8");return JSON.parse(D)}catch(D){if(D.code==="ENOENT")return{};throw new Q(`Failed to read config at ${U}: ${D.message}`,z.USAGE)}}async function KU(U){let D=await iU(),O=(U.apiUrl||process.env.DUN_API_URL||D.apiUrl||nU).replace(/\/$/,""),A=U.token||process.env.DUN_TOKEN||D.token,G=U.workspace||process.env.DUN_WORKSPACE_ID||D.defaultWorkspaceId||void 0,$=process.env.DUN_JSON==="0",V=!process.stdout.isTTY,K=$?!1:U.json===!0||process.env.DUN_JSON==="1"||process.env.CI==="true"||process.env.CI==="1"||V;return{apiUrl:O,token:A,workspaceId:G,json:K,debug:U.debug===!0||process.env.DUN_DEBUG==="1",quiet:U.quiet===!0,yes:U.yes===!0||process.env.CI==="true"||process.env.CI==="1",config:D}}function VU(U){let D={},O=[],A=0,G=!0;while(A<U.length){let Z=U[A];if(G&&Z==="--"){G=!1,A+=1;continue}if(G&&Z.startsWith("--")){let J=Z.indexOf("=");if(J!==-1){D[Z.slice(2,J)]=Z.slice(J+1),A+=1;continue}let q=Z.slice(2),Y=U[A+1];if(Y!==void 0&&!Y.startsWith("-"))D[q]=Y,A+=2;else D[q]=!0,A+=1;continue}if(G&&Z.startsWith("-")&&Z.length>1&&Z!=="-"){let J=Z.slice(1);if(J.length>1&&!J.includes("=")){for(let H of J)D[H]=!0;A+=1;continue}let q=J,Y=U[A+1];if(Y!==void 0&&!Y.startsWith("-"))D[q]=Y,A+=2;else D[q]=!0,A+=1;continue}O.push(Z),A+=1}let $=O[0],V,K,X=1;if($==="memory"&&(O[1]==="collections"||O[1]==="collection"))K="collections",V=O[2],X=V?3:2;else V=O[1],X=V?2:1;return{group:$,action:V,nest:K,flags:D,positionals:O.slice(X)}}function m(U,...D){for(let O of D){let A=U[O];if(typeof A==="string"&&A.length>0)return A}return}function B(U,...D){for(let O of D){let A=U[O];if(A===!0)return!0;if(typeof A==="string"){let G=A.toLowerCase();if(G==="1"||G==="true"||G==="yes")return!0}}return!1}function n(U){return B(U,"help","h")}function f(U){return B(U,"version","v","V")}function x(U,D){if(D.quiet&&!D.json)return;if(D.json||!process.stdout.isTTY){process.stdout.write(`${JSON.stringify({ok:!0,data:U},null,2)}
4
4
  `);return}if(U===void 0||U===null){process.stdout.write(`ok
5
5
  `);return}if(typeof U==="string"){process.stdout.write(`${U}
6
- `);return}process.stdout.write(`${cU(U,D.emptyMessage)}
7
- `)}function zU(U,D){let{message:O,code:K,details:G}=xU(U);if(D.json||!process.stdout.isTTY)process.stdout.write(`${JSON.stringify({ok:!1,error:{code:iU(K),message:O,details:G??null}},null,2)}
6
+ `);return}process.stdout.write(`${oU(U,D.emptyMessage)}
7
+ `)}function zU(U,D){let{message:O,code:A,details:G}=cU(U);if(D.json||!process.stdout.isTTY)process.stdout.write(`${JSON.stringify({ok:!1,error:{code:xU(A),message:O,details:G??null}},null,2)}
8
8
  `);else if(process.stderr.write(`error: ${O}
9
9
  `),G!==void 0&&process.env.DUN_DEBUG==="1")process.stderr.write(`${JSON.stringify(G,null,2)}
10
- `);return K}function iU(U){switch(U){case z.AUTH:return"auth";case z.FORBIDDEN:return"forbidden";case z.VALIDATION:return"validation";case z.NOT_FOUND:return"not_found";case z.RATE_LIMITED:return"rate_limited";case z.NETWORK:return"network";default:return"usage"}}function xU(U){if(U instanceof Q)return{message:U.message,code:U.code,details:U.details};if(U instanceof Error)return{message:U.message,code:z.USAGE};return{message:String(U),code:z.USAGE}}function cU(U,D){if(Array.isArray(U)){if(U.length===0)return D??"(empty)";if(U.every((O)=>O&&typeof O==="object"))return GU(U,D);return U.map((O)=>`- ${i(O)}`).join(`
11
- `)}if(U&&typeof U==="object"){let O=U;if(Array.isArray(O.items)){if(O.items.length===0&&D)return D;let K=typeof O.total==="number"?`total=${O.total} showing=${O.items.length}`:void 0,G=GU(O.items,D);return K?`${K}
12
- ${G}`:G}return Object.entries(O).map(([K,G])=>`${K}: ${i(G)}`).join(`
13
- `)}return String(U)}function GU(U,D){if(U.length===0)return D??"(empty)";let O=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],K=new Set;for(let Z of U)for(let J of Object.keys(Z))K.add(J);let G=[...O.filter((Z)=>K.has(Z)),...[...K].filter((Z)=>!O.includes(Z)).slice(0,6)].slice(0,8),$=U.map((Z)=>G.map((J)=>gU(i(Z[J]),J==="content"||J==="text"?48:28))),A=G.map((Z,J)=>Math.max(Z.length,...$.map((N)=>N[J]?.length??0))),V=G.map((Z,J)=>Z.padEnd(A[J])).join(" "),X=$.map((Z)=>Z.map((J,N)=>J.padEnd(A[N])).join(" ")).join(`
14
- `);return`${V}
15
- ${X}`}function i(U){if(U===null||U===void 0)return"";if(typeof U==="string")return U.replace(/\s+/g," ").trim();if(typeof U==="number"||typeof U==="boolean")return String(U);if(U instanceof Date)return U.toISOString();if(typeof U==="object"){let D=U;if(typeof D.id==="string"&&typeof D.name==="string")return`${D.name} (${D.id})`;if(typeof D.status==="string")return D.status;return JSON.stringify(U)}return String(U)}function gU(U,D){if(U.length<=D)return U;return`${U.slice(0,Math.max(0,D-1))}…`}var _={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class c extends Error{code;details;constructor(U,D=_.USAGE,O){super(U);this.name="CliError",this.code=D,this.details=O}}var QU=`dun schedule & publish social posts
10
+ `);return A}function xU(U){switch(U){case z.AUTH:return"auth";case z.FORBIDDEN:return"forbidden";case z.VALIDATION:return"validation";case z.NOT_FOUND:return"not_found";case z.RATE_LIMITED:return"rate_limited";case z.NETWORK:return"network";default:return"usage"}}function cU(U){if(U instanceof Q)return{message:U.message,code:U.code,details:U.details};if(U instanceof Error)return{message:U.message,code:z.USAGE};return{message:String(U),code:z.USAGE}}function oU(U,D){if(Array.isArray(U)){if(U.length===0)return D??"(empty)";if(U.every((O)=>O&&typeof O==="object"))return GU(U,D);return U.map((O)=>`- ${i(O)}`).join(`
11
+ `)}if(U&&typeof U==="object"){let O=U;if(Array.isArray(O.items)){if(O.items.length===0&&D)return D;let A=typeof O.total==="number"?`total=${O.total} showing=${O.items.length}`:void 0,G=GU(O.items,D);return A?`${A}
12
+ ${G}`:G}return Object.entries(O).map(([A,G])=>`${A}: ${i(G)}`).join(`
13
+ `)}return String(U)}function GU(U,D){if(U.length===0)return D??"(empty)";let O=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],A=new Set;for(let Z of U)for(let J of Object.keys(Z))A.add(J);let G=[...O.filter((Z)=>A.has(Z)),...[...A].filter((Z)=>!O.includes(Z)).slice(0,6)].slice(0,8),$=U.map((Z)=>G.map((J)=>eU(i(Z[J]),J==="content"||J==="text"?48:28))),V=G.map((Z,J)=>Math.max(Z.length,...$.map((q)=>q[J]?.length??0))),K=G.map((Z,J)=>Z.padEnd(V[J])).join(" "),X=$.map((Z)=>Z.map((J,q)=>J.padEnd(V[q])).join(" ")).join(`
14
+ `);return`${K}
15
+ ${X}`}function i(U){if(U===null||U===void 0)return"";if(typeof U==="string")return U.replace(/\s+/g," ").trim();if(typeof U==="number"||typeof U==="boolean")return String(U);if(U instanceof Date)return U.toISOString();if(typeof U==="object"){let D=U;if(typeof D.id==="string"&&typeof D.name==="string")return`${D.name} (${D.id})`;if(typeof D.status==="string")return D.status;return JSON.stringify(U)}return String(U)}function eU(U,D){if(U.length<=D)return U;return`${U.slice(0,Math.max(0,D-1))}...`}var I={OK:0,USAGE:1,AUTH:2,FORBIDDEN:3,VALIDATION:4,NOT_FOUND:5,RATE_LIMITED:6,NETWORK:7};class c extends Error{code;details;constructor(U,D=I.USAGE,O){super(U);this.name="CliError",this.code=D,this.details=O}}var QU=`dun - schedule & publish social posts
16
16
 
17
17
  Usage:
18
18
  dun <command> [flags]
@@ -64,6 +64,13 @@ Memory:
64
64
  dun memory search --prompt "..." --collections <id,id> [--top-k 5]
65
65
  dun memory delete <id> --collection <id> [--yes]
66
66
 
67
+ Personalization (workspace AI voice):
68
+ dun personalization get
69
+ dun personalization set --description "..."
70
+ dun personalization set --file voice.md
71
+ dun personalization set --clear
72
+ # requires PAT scope workspace:write for set (owner/admin)
73
+
67
74
  Agent skills:
68
75
  dun skill install <agent|all>
69
76
  dun skill list
@@ -86,10 +93,10 @@ Env:
86
93
  DUN_TOKEN DUN_WORKSPACE_ID DUN_API_URL DUN_CONFIG DUN_JSON=1 DUN_DEBUG=1
87
94
 
88
95
  Exit codes:
89
- 0 ok · 1 usage · 2 auth · 3 forbidden · 4 validation · 5 not found · 6 rate limit · 7 network
96
+ 0 ok | 1 usage | 2 auth | 3 forbidden | 4 validation | 5 not found | 6 rate limit | 7 network
90
97
 
91
98
  Docs: https://dunsocial.com
92
- `;function g(U){return{auth:`dun auth
99
+ `;function o(U){return{auth:`dun auth
93
100
 
94
101
  login [--token] Device-code browser login (default) or --token for CI
95
102
  logout Clear local config
@@ -111,9 +118,18 @@ Docs: https://dunsocial.com
111
118
  delete <id> [--yes]
112
119
  x-cap
113
120
  `,drafts:"dun drafts list|get|create|update|delete",media:"dun media list|get|upload|delete",memory:`dun memory collections list|create
114
- dun memory list|save|search|delete`,skill:`dun skill install <claude|codex|cursor|pi|opencode|agents|copilot|windsurf|goose|gemini|amp|grok|continue|antigravity|all>
121
+ dun memory list|save|search|delete`,personalization:`dun personalization
122
+
123
+ get Show workspace AI voice profile
124
+ set --description "..." Set voice from a paragraph
125
+ set --file <path> Set voice from a markdown/text file
126
+ set --clear Restore system defaults
127
+
128
+ Workspace-scoped (not per-user). set requires owner/admin + PAT scope workspace:write.
129
+ Alias: dun voice ...
130
+ `,voice:"Alias of personalization",skill:`dun skill install <claude|codex|cursor|pi|opencode|agents|copilot|windsurf|goose|gemini|amp|grok|continue|antigravity|all>
115
131
  dun skill list
116
- dun skill path`}[U]??`dun schedule & publish social posts
132
+ dun skill path`}[U]??`dun - schedule & publish social posts
117
133
 
118
134
  Usage:
119
135
  dun <command> [flags]
@@ -165,6 +181,13 @@ Memory:
165
181
  dun memory search --prompt "..." --collections <id,id> [--top-k 5]
166
182
  dun memory delete <id> --collection <id> [--yes]
167
183
 
184
+ Personalization (workspace AI voice):
185
+ dun personalization get
186
+ dun personalization set --description "..."
187
+ dun personalization set --file voice.md
188
+ dun personalization set --clear
189
+ # requires PAT scope workspace:write for set (owner/admin)
190
+
168
191
  Agent skills:
169
192
  dun skill install <agent|all>
170
193
  dun skill list
@@ -187,55 +210,55 @@ Env:
187
210
  DUN_TOKEN DUN_WORKSPACE_ID DUN_API_URL DUN_CONFIG DUN_JSON=1 DUN_DEBUG=1
188
211
 
189
212
  Exit codes:
190
- 0 ok · 1 usage · 2 auth · 3 forbidden · 4 validation · 5 not found · 6 rate limit · 7 network
213
+ 0 ok | 1 usage | 2 auth | 3 forbidden | 4 validation | 5 not found | 6 rate limit | 7 network
191
214
 
192
215
  Docs: https://dunsocial.com
193
- `}import{mkdir as oU,readFile as i0,writeFile as eU,chmod as rU,unlink as lU}from"node:fs/promises";import{dirname as sU,join as XU}from"node:path";import{homedir as tU}from"node:os";var M="0.4.0";function o(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let U=process.env.XDG_CONFIG_HOME,D=U?U:XU(tU(),".config");return XU(D,"dunsocial","config.json")}function j(){return o()}async function b(U){let D=o();await oU(sU(D),{recursive:!0});let O={...U,updatedAt:new Date().toISOString()};await eU(D,`${JSON.stringify(O,null,2)}
194
- `,"utf8");try{await rU(D,384)}catch{}}async function ZU(){let U=o();try{await lU(U)}catch(D){if(D.code!=="ENOENT")throw new Q(`Failed to clear config: ${D.message}`,z.USAGE)}}function w(U){if(!U.token)throw new Q("Not authenticated. Run `dun auth login` (or `dun auth login --token <token>` / set DUN_TOKEN).",z.AUTH);return U.token}function H(U){if(!U.workspaceId)throw new Q("No workspace selected. Run `dun workspace use <id|slug>` or pass --workspace / DUN_WORKSPACE_ID.",z.VALIDATION);return U.workspaceId}var JU=[" ___ ___ _ _ "," | \\ _ _ _ _ / __| ___ __(_)__ _| |"," | |) | || | ' \\__ \\/ _ \\/ _| / _` | |"," |___/ \\_,_|_||_|___/\\___/\\__|_\\__,_|_|"].join(`
195
- `),RU=[" _ "," __| |_ _ _ _ "," / _` | || | ' \\ "," \\__,_|\\_,_|_||_|",""," DunSocial"].join(`
196
- `),aU="DunSocial",U0=YU(JU),O0=YU(RU);function YU(U){return Math.max(...U.split(`
197
- `).map((D)=>D.length))}function e(U=process.stderr){if(!U.isTTY)return!1;if(process.env.CI==="true"||process.env.CI==="1")return!1;if(process.env.GITHUB_ACTIONS||process.env.TF_BUILD||process.env.GITLAB_CI)return!1;if(process.env.DUN_JSON==="1")return!1;return!0}function qU(U=process.stdout.columns??80){let D=U>0?U:80;if(D>=U0)return JU;if(D>=O0)return RU;return aU}function D0(U=process.stdout.columns??80){return`${qU(U)}
216
+ `}import{mkdir as gU,readFile as c0,writeFile as rU,chmod as lU,unlink as sU}from"node:fs/promises";import{dirname as tU,join as XU}from"node:path";import{homedir as aU}from"node:os";var M="0.4.0";function e(){if(process.env.DUN_CONFIG)return process.env.DUN_CONFIG;let U=process.env.XDG_CONFIG_HOME,D=U?U:XU(aU(),".config");return XU(D,"dunsocial","config.json")}function j(){return e()}async function b(U){let D=e();await gU(tU(D),{recursive:!0});let O={...U,updatedAt:new Date().toISOString()};await rU(D,`${JSON.stringify(O,null,2)}
217
+ `,"utf8");try{await lU(D,384)}catch{}}async function ZU(){let U=e();try{await sU(U)}catch(D){if(D.code!=="ENOENT")throw new Q(`Failed to clear config: ${D.message}`,z.USAGE)}}function N(U){if(!U.token)throw new Q("Not authenticated. Run `dun auth login` (or `dun auth login --token <token>` / set DUN_TOKEN).",z.AUTH);return U.token}function P(U){if(!U.workspaceId)throw new Q("No workspace selected. Run `dun workspace use <id|slug>` or pass --workspace / DUN_WORKSPACE_ID.",z.VALIDATION);return U.workspaceId}var JU=["█▀▀▄ █▀▀▄ █▀▀▀ █▀▀█ █▀▀▀ ▀█▀ █▀▀█ ","█ █ █ █ █ █ ▀▀▀█ █ █ █ █ █▀▀█ █","▀▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀"].join(`
218
+ `),RU=["█▀▀▄ █ █ █▀▀▄"," ","▀▀▀▀ ▀▀▀▀ ▀ ▀",""," DunSocial"].join(`
219
+ `),U0="DunSocial",O0=wU(JU),D0=wU(RU);function wU(U){return Math.max(...U.split(`
220
+ `).map((D)=>D.length))}function g(U=process.stderr){if(!U.isTTY)return!1;if(process.env.CI==="true"||process.env.CI==="1")return!1;if(process.env.GITHUB_ACTIONS||process.env.TF_BUILD||process.env.GITLAB_CI)return!1;if(process.env.DUN_JSON==="1")return!1;return!0}function YU(U=process.stdout.columns??80){let D=U>0?U:80;if(D>=O0)return JU;if(D>=D0)return RU;return U0}function $0(U=process.stdout.columns??80){return`${YU(U)}
198
221
 
199
- Schedule & publish from the terminal · v${M}`}function NU(U=process.stderr,D={}){if(!e(U))return;let O=D.columns??U.columns??process.stdout.columns??80,K=D.tagline?D0(O):qU(O);U.write(`
200
- ${K}
222
+ Schedule & publish from the terminal | v${M}`}function qU(U=process.stderr,D={}){if(!g(U))return;let O=D.columns??U.columns??process.stdout.columns??80,A=D.tagline?$0(O):YU(O);U.write(`
223
+ ${A}
201
224
 
202
225
  `)}function R(U,D){if(D.quiet&&!D.json)return;if(D.json||!process.stdout.isTTY){process.stdout.write(`${JSON.stringify({ok:!0,data:U},null,2)}
203
226
  `);return}if(U===void 0||U===null){process.stdout.write(`ok
204
227
  `);return}if(typeof U==="string"){process.stdout.write(`${U}
205
- `);return}process.stdout.write(`${$0(U,D.emptyMessage)}
206
- `)}function $0(U,D){if(Array.isArray(U)){if(U.length===0)return D??"(empty)";if(U.every((O)=>O&&typeof O==="object"))return TU(U,D);return U.map((O)=>`- ${r(O)}`).join(`
207
- `)}if(U&&typeof U==="object"){let O=U;if(Array.isArray(O.items)){if(O.items.length===0&&D)return D;let K=typeof O.total==="number"?`total=${O.total} showing=${O.items.length}`:void 0,G=TU(O.items,D);return K?`${K}
208
- ${G}`:G}return Object.entries(O).map(([K,G])=>`${K}: ${r(G)}`).join(`
209
- `)}return String(U)}function TU(U,D){if(U.length===0)return D??"(empty)";let O=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],K=new Set;for(let Z of U)for(let J of Object.keys(Z))K.add(J);let G=[...O.filter((Z)=>K.has(Z)),...[...K].filter((Z)=>!O.includes(Z)).slice(0,6)].slice(0,8),$=U.map((Z)=>G.map((J)=>K0(r(Z[J]),J==="content"||J==="text"?48:28))),A=G.map((Z,J)=>Math.max(Z.length,...$.map((N)=>N[J]?.length??0))),V=G.map((Z,J)=>Z.padEnd(A[J])).join(" "),X=$.map((Z)=>Z.map((J,N)=>J.padEnd(A[N])).join(" ")).join(`
210
- `);return`${V}
211
- ${X}`}function r(U){if(U===null||U===void 0)return"";if(typeof U==="string")return U.replace(/\s+/g," ").trim();if(typeof U==="number"||typeof U==="boolean")return String(U);if(U instanceof Date)return U.toISOString();if(typeof U==="object"){let D=U;if(typeof D.id==="string"&&typeof D.name==="string")return`${D.name} (${D.id})`;if(typeof D.status==="string")return D.status;return JSON.stringify(U)}return String(U)}function K0(U,D){if(U.length<=D)return U;return`${U.slice(0,Math.max(0,D-1))}…`}function d(U,D,O){if(!U)return;if(O!==void 0){process.stderr.write(`[debug] ${D} ${JSON.stringify(O)}
228
+ `);return}process.stdout.write(`${A0(U,D.emptyMessage)}
229
+ `)}function A0(U,D){if(Array.isArray(U)){if(U.length===0)return D??"(empty)";if(U.every((O)=>O&&typeof O==="object"))return TU(U,D);return U.map((O)=>`- ${r(O)}`).join(`
230
+ `)}if(U&&typeof U==="object"){let O=U;if(Array.isArray(O.items)){if(O.items.length===0&&D)return D;let A=typeof O.total==="number"?`total=${O.total} showing=${O.items.length}`:void 0,G=TU(O.items,D);return A?`${A}
231
+ ${G}`:G}return Object.entries(O).map(([A,G])=>`${A}: ${r(G)}`).join(`
232
+ `)}return String(U)}function TU(U,D){if(U.length===0)return D??"(empty)";let O=["id","name","slug","status","providerName","username","displayName","role","scheduledAt","content","text","originalFilename","score","collectionId"],A=new Set;for(let Z of U)for(let J of Object.keys(Z))A.add(J);let G=[...O.filter((Z)=>A.has(Z)),...[...A].filter((Z)=>!O.includes(Z)).slice(0,6)].slice(0,8),$=U.map((Z)=>G.map((J)=>K0(r(Z[J]),J==="content"||J==="text"?48:28))),V=G.map((Z,J)=>Math.max(Z.length,...$.map((q)=>q[J]?.length??0))),K=G.map((Z,J)=>Z.padEnd(V[J])).join(" "),X=$.map((Z)=>Z.map((J,q)=>J.padEnd(V[q])).join(" ")).join(`
233
+ `);return`${K}
234
+ ${X}`}function r(U){if(U===null||U===void 0)return"";if(typeof U==="string")return U.replace(/\s+/g," ").trim();if(typeof U==="number"||typeof U==="boolean")return String(U);if(U instanceof Date)return U.toISOString();if(typeof U==="object"){let D=U;if(typeof D.id==="string"&&typeof D.name==="string")return`${D.name} (${D.id})`;if(typeof D.status==="string")return D.status;return JSON.stringify(U)}return String(U)}function K0(U,D){if(U.length<=D)return U;return`${U.slice(0,Math.max(0,D-1))}...`}function S(U,D,O){if(!U)return;if(O!==void 0){process.stderr.write(`[debug] ${D} ${JSON.stringify(O)}
212
235
  `);return}process.stderr.write(`[debug] ${D}
213
- `)}class WU{ctx;constructor(U){this.ctx=U}async request(U){let D=U.method??"GET",O=new URL(U.path.startsWith("http")?U.path:`${this.ctx.apiUrl}${U.path}`);if(U.query)for(let[X,Z]of Object.entries(U.query)){if(Z===void 0||Z===null||Z==="")continue;O.searchParams.set(X,String(Z))}let K={Accept:"application/json","User-Agent":`dunsocial-cli/${M}`,...U.headers??{}};if(U.auth!==!1)K.Authorization=`Bearer ${w(this.ctx)}`;if(U.workspace)K["X-Workspace-Id"]=H(this.ctx);let G;if(U.body!==void 0)K["Content-Type"]="application/json",G=JSON.stringify(U.body);d(this.ctx.debug,`${D} ${O.toString()}`);let $;try{$=await fetch(O,{method:D,headers:K,body:G})}catch(X){throw new Q(`Network error: ${X.message}`,z.NETWORK,{cause:String(X)})}let A=await $.text(),V=null;if(A)try{V=JSON.parse(A)}catch{V=null}if(d(this.ctx.debug,`← ${$.status}`,{ok:$.ok,bodyPreview:A.slice(0,300)}),!$.ok){let X=V?.error||V?.message||(A?A.slice(0,300):`HTTP ${$.status}`);throw new Q(X,$U($.status),{status:$.status,body:V??A})}if(V&&typeof V==="object"&&"success"in V){if(V.success===!1)throw new Q(V.error||V.message||"Request failed",z.VALIDATION,V);return V.data}return V??void 0}get(U,D){return this.request({...D,method:"GET",path:U})}post(U,D,O){return this.request({...O,method:"POST",path:U,body:D})}patch(U,D,O){return this.request({...O,method:"PATCH",path:U,body:D})}delete(U,D,O){return this.request({...O,method:"DELETE",path:U,body:D})}}function T(U){return new WU(U)}function Y(U,...D){for(let O of D){let K=U[O];if(typeof K==="string"&&K.length>0)return K}return}function F(U,...D){for(let O of D){let K=U[O];if(K===!0)return!0;if(typeof K==="string"){let G=K.toLowerCase();if(G==="1"||G==="true"||G==="yes")return!0}}return!1}function P(U,...D){let O=Y(U,...D);if(O===void 0)return;let K=Number(O);if(!Number.isFinite(K))throw Error(`Invalid number for --${D[0]}: ${O}`);return K}function u(U,...D){let O=Y(U,...D);if(O===void 0)return;return O.split(",").map((K)=>K.trim()).filter(Boolean)}var V0=[" ___ ___ _ _ "," | \\ _ _ _ _ / __| ___ __(_)__ _| |"," | |) | || | ' \\__ \\/ _ \\/ _| / _` | |"," |___/ \\_,_|_||_|___/\\___/\\__|_\\__,_|_|"].join(`
214
- `),wU=[" _ "," __| |_ _ _ _ "," / _` | || | ' \\ "," \\__,_|\\_,_|_||_|",""," DunSocial"].join(`
215
- `);var KO=HU(V0),VO=HU(wU);function HU(U){return Math.max(...U.split(`
216
- `).map((D)=>D.length))}function S(U=process.stderr){if(!U.isTTY)return!1;if(process.env.CI==="true"||process.env.CI==="1")return!1;if(process.env.GITHUB_ACTIONS||process.env.TF_BUILD||process.env.GITLAB_CI)return!1;if(process.env.DUN_JSON==="1")return!1;return!0}function FU(){return wU}async function PU(U,D,O,K){switch(D){case"login":return A0(U,O);case"logout":return Q0(U);case"whoami":return X0(U);case"status":return Z0(U);default:throw new Q(`Unknown auth command "${D??""}". Try: login, logout, whoami, status`,z.USAGE)}}async function A0(U,D){let O=Y(D,"token")||process.env.DUN_TOKEN;if(O)return G0(U,O);return z0(U,D)}function E(U){if(U)return"Logged in.";return`Logged in.
217
- Next: dun workspace list && dun workspace use <id|slug>`}async function G0(U,D){let O={...U,token:D},G=await T(O).get("/api/user/profile");if(await b({...U.config,apiUrl:U.apiUrl,token:D,defaultWorkspaceId:U.config.defaultWorkspaceId,defaultWorkspaceSlug:U.config.defaultWorkspaceSlug,defaultWorkspaceName:U.config.defaultWorkspaceName}),!U.json&&S(process.stdout)){R(E(Boolean(U.config.defaultWorkspaceId)),U);return}R({user:{id:G.id,name:G.name,email:G.email},configPath:j(),method:"token",message:E(Boolean(U.config.defaultWorkspaceId))},U)}async function z0(U,D){let O=T({...U,token:void 0}),K=await O.post("/api/cli/auth/start",{clientName:"dun-cli"},{auth:!1}),G=!F(D,"no-browser");if(!U.json){if(S(process.stderr))process.stderr.write(`
218
- ${FU()}
236
+ `)}class NU{ctx;constructor(U){this.ctx=U}async request(U){let D=U.method??"GET",O=new URL(U.path.startsWith("http")?U.path:`${this.ctx.apiUrl}${U.path}`);if(U.query)for(let[X,Z]of Object.entries(U.query)){if(Z===void 0||Z===null||Z==="")continue;O.searchParams.set(X,String(Z))}let A={Accept:"application/json","User-Agent":`dunsocial-cli/${M}`,...U.headers??{}};if(U.auth!==!1)A.Authorization=`Bearer ${N(this.ctx)}`;if(U.workspace)A["X-Workspace-Id"]=P(this.ctx);let G;if(U.body!==void 0)A["Content-Type"]="application/json",G=JSON.stringify(U.body);S(this.ctx.debug,`${D} ${O.toString()}`);let $;try{$=await fetch(O,{method:D,headers:A,body:G})}catch(X){throw new Q(`Network error: ${X.message}`,z.NETWORK,{cause:String(X)})}let V=await $.text(),K=null;if(V)try{K=JSON.parse(V)}catch{K=null}if(S(this.ctx.debug,`<- ${$.status}`,{ok:$.ok,bodyPreview:V.slice(0,300)}),!$.ok){let X=K?.error||K?.message||(V?V.slice(0,300):`HTTP ${$.status}`);throw new Q(X,$U($.status),{status:$.status,body:K??V})}if(K&&typeof K==="object"&&"success"in K){if(K.success===!1)throw new Q(K.error||K.message||"Request failed",z.VALIDATION,K);return K.data}return K??void 0}get(U,D){return this.request({...D,method:"GET",path:U})}post(U,D,O){return this.request({...O,method:"POST",path:U,body:D})}patch(U,D,O){return this.request({...O,method:"PATCH",path:U,body:D})}delete(U,D,O){return this.request({...O,method:"DELETE",path:U,body:D})}}function T(U){return new NU(U)}function w(U,...D){for(let O of D){let A=U[O];if(typeof A==="string"&&A.length>0)return A}return}function u(U,...D){for(let O of D){let A=U[O];if(A===!0)return!0;if(typeof A==="string"){let G=A.toLowerCase();if(G==="1"||G==="true"||G==="yes")return!0}}return!1}function F(U,...D){let O=w(U,...D);if(O===void 0)return;let A=Number(O);if(!Number.isFinite(A))throw Error(`Invalid number for --${D[0]}: ${O}`);return A}function _(U,...D){let O=w(U,...D);if(O===void 0)return;return O.split(",").map((A)=>A.trim()).filter(Boolean)}var V0=["█▀▀▄ █▀▀▄ █▀▀▀ █▀▀█ █▀▀▀ ▀█▀ █▀▀█ ","█ █ █ █ █ █ ▀▀▀█ █ █ █ █ █▀▀█ █","▀▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀"].join(`
237
+ `),WU=["█▀▀▄ █ █ █▀▀▄"," ","▀▀▀▀ ▀▀▀▀ ▀ ▀",""," DunSocial"].join(`
238
+ `);var VO=PU(V0),GO=PU(WU);function PU(U){return Math.max(...U.split(`
239
+ `).map((D)=>D.length))}function d(U=process.stderr){if(!U.isTTY)return!1;if(process.env.CI==="true"||process.env.CI==="1")return!1;if(process.env.GITHUB_ACTIONS||process.env.TF_BUILD||process.env.GITLAB_CI)return!1;if(process.env.DUN_JSON==="1")return!1;return!0}function uU(){return WU}async function HU(U,D,O,A){switch(D){case"login":return G0(U,O);case"logout":return X0(U);case"whoami":return Z0(U);case"status":return J0(U);default:throw new Q(`Unknown auth command "${D??""}". Try: login, logout, whoami, status`,z.USAGE)}}async function G0(U,D){let O=w(D,"token")||process.env.DUN_TOKEN;if(O)return z0(U,O);return Q0(U,D)}function E(U){if(U)return"Logged in.";return`Logged in.
240
+ Next: dun workspace list && dun workspace use <id|slug>`}async function z0(U,D){let O={...U,token:D},G=await T(O).get("/api/user/profile");if(await b({...U.config,apiUrl:U.apiUrl,token:D,defaultWorkspaceId:U.config.defaultWorkspaceId,defaultWorkspaceSlug:U.config.defaultWorkspaceSlug,defaultWorkspaceName:U.config.defaultWorkspaceName}),!U.json&&d(process.stdout)){R(E(Boolean(U.config.defaultWorkspaceId)),U);return}R({user:{id:G.id,name:G.name,email:G.email},configPath:j(),method:"token",message:E(Boolean(U.config.defaultWorkspaceId))},U)}async function Q0(U,D){let O=T({...U,token:void 0}),A=await O.post("/api/cli/auth/start",{clientName:"dun-cli"},{auth:!1}),G=!u(D,"no-browser");if(!U.json){if(d(process.stderr))process.stderr.write(`
241
+ ${uU()}
219
242
 
220
243
  `);else process.stderr.write(`
221
244
  DunSocial CLI login
222
245
  `),process.stderr.write(`───────────────────
223
246
  `);process.stderr.write(`In your browser, open:
224
- ${K.verificationUrlComplete}
225
-
226
- `),process.stderr.write(`Or go to ${K.verificationUrl} and enter code:
227
- `),process.stderr.write(` ${K.userCode}
228
-
229
- `),process.stderr.write(`Waiting for approval
230
- `)}else process.stderr.write(JSON.stringify({ok:!0,phase:"waiting",userCode:K.userCode,verificationUrlComplete:K.verificationUrlComplete})+`
231
- `);if(G&&process.stdout.isTTY)try{await R0(K.verificationUrlComplete)}catch{}let $=Math.max(3,K.interval||5)*1000,A=Date.now()+(K.expiresIn||900)*1000;while(Date.now()<A){await J0($);let V=await O.post("/api/cli/auth/poll",{deviceCode:K.deviceCode},{auth:!1});if(V.status==="pending")continue;if(V.status==="expired")throw new Q("Login expired. Run dun auth login again.",z.AUTH);if(V.status==="denied")throw new Q("Login was denied in the browser.",z.AUTH);if(V.status==="approved"){let X=V.token,Z={...U,token:X},J=T(Z),N=await J.get("/api/user/profile"),q={...U.config,apiUrl:U.apiUrl,token:X,defaultWorkspaceId:V.workspaceId||U.config.defaultWorkspaceId,defaultWorkspaceSlug:U.config.defaultWorkspaceSlug,defaultWorkspaceName:U.config.defaultWorkspaceName};if(V.workspaceId)try{let B=(await J.get("/api/workspaces")).find((L)=>L.id===V.workspaceId);if(B)q.defaultWorkspaceId=B.id,q.defaultWorkspaceSlug=B.slug,q.defaultWorkspaceName=B.name}catch{}await b(q);let I=Boolean(q.defaultWorkspaceId);if(!U.json&&S(process.stdout)){R(E(I),U);return}R({user:{id:N.id,name:N.name,email:N.email},workspaceId:q.defaultWorkspaceId??null,configPath:j(),method:"device",message:E(I)},U);return}}throw new Q("Login timed out. Run dun auth login again.",z.AUTH)}async function Q0(U){if(await ZU(),!U.json&&S(process.stdout)){R("Logged out. Config cleared.",U);return}R({message:"Logged out. Config cleared.",configPath:j()},U)}async function X0(U){w(U);let O=await T(U).get("/api/user/profile");R({id:O.id,name:O.name,email:O.email,image:O.image??null,apiUrl:U.apiUrl,workspaceId:U.workspaceId??null},U)}async function Z0(U){let D=Boolean(U.token),O=null,K=null;if(D)try{O=await T(U).get("/api/user/profile")}catch(G){K=G instanceof Error?G.message:String(G)}R({authenticated:Boolean(O),tokenPresent:D,apiUrl:U.apiUrl,workspaceId:U.workspaceId??null,workspaceSlug:U.config.defaultWorkspaceSlug??null,workspaceName:U.config.defaultWorkspaceName??null,configPath:j(),user:O?{id:O.id,name:O.name,email:O.email}:null,error:K},U)}function J0(U){return new Promise((D)=>setTimeout(D,U))}async function R0(U){let D=process.platform,O=D==="darwin"?["open",U]:D==="win32"?["cmd","/c","start","",U]:["xdg-open",U];await Bun.spawn(O,{stdout:"ignore",stderr:"ignore"}).exited}async function uU(U,D,O,K){w(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/workspaces");R($.map((A)=>({id:A.id,name:A.name,slug:A.slug,role:A.role,timezone:A.timezone,plan:A.subscription?.planName??null,status:A.subscription?.status??null})),{...U,emptyMessage:"No workspaces yet."});return}case"use":{let $=K[0];if(!$)throw new Q("Usage: dun workspace use <id|slug>",z.USAGE);let V=(await G.get("/api/workspaces")).find((X)=>X.id===$||X.slug===$);if(!V)throw new Q(`Workspace not found: ${$}. Run dun workspace list`,z.NOT_FOUND);await b({...U.config,apiUrl:U.apiUrl,token:U.token,defaultWorkspaceId:V.id,defaultWorkspaceSlug:V.slug,defaultWorkspaceName:V.name}),R({id:V.id,name:V.name,slug:V.slug,role:V.role,message:`Default workspace set to ${V.name}`},U);return}case"current":{if(!U.workspaceId&&!U.config.defaultWorkspaceId)throw new Q("No workspace selected. Run dun workspace use <id|slug>",z.VALIDATION);let $=U.workspaceId||U.config.defaultWorkspaceId;try{let V=(await G.get("/api/workspaces")).find((X)=>X.id===$||X.slug===$);if(V){R({id:V.id,name:V.name,slug:V.slug,role:V.role,timezone:V.timezone},U);return}}catch{}R({id:$,name:U.config.defaultWorkspaceName??null,slug:U.config.defaultWorkspaceSlug??null},U);return}default:throw new Q(`Unknown workspace command "${D??""}". Try: list, use, current`,z.USAGE)}}async function _U(U,D,O,K){w(U),H(U);let G=T(U);switch(D){case void 0:case"list":case"ls":{let $=await G.get("/api/social-accounts",{workspace:!0}),A=Y(O,"platform","p")?.toLowerCase(),V=A?$.filter((X)=>{let Z=(X.providerName||"").toLowerCase();return({x:["x","twitter"],twitter:["x","twitter"],linkedin:["linkedin"],"linkedin-business":["linkedin business","linkedin-business","linkedin_business"],bluesky:["bluesky"],threads:["threads"],reddit:["reddit"],pinterest:["pinterest"],instagram:["instagram"],youtube:["youtube"]}[A]??[A]).some((q)=>Z.includes(q))}):$;R(V.map((X)=>({id:X.id,providerName:X.providerName,username:X.username??null,displayName:X.displayName??null,isConnected:X.isConnected,expiresAt:X.expiresAt??null})),{...U,emptyMessage:A?`No ${A} accounts connected. Connect one in the DunSocial dashboard.`:"No accounts connected. Connect one in the DunSocial dashboard."});return}default:throw new Q(`Unknown accounts command "${D}". Try: list`,z.USAGE)}}var Y0=/^(\d+)\s*(s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days|w|week|weeks)$/i;function l(U,D=new Date){let O=U.trim();if(!O)throw new Q("Empty time value",z.VALIDATION);let K=O.match(Y0);if(K){let A=Number(K[1]),V=K[2].toLowerCase(),X=q0(V)*A;return new Date(D.getTime()+X)}let G=O.match(/^in\s+(.+)$/i);if(G)return l(G[1],D);let $=new Date(O);if(Number.isNaN($.getTime()))throw new Q(`Invalid time "${U}". Use ISO-8601 (2026-06-01T09:00:00Z) or relative (2h, 30m, 1d).`,z.VALIDATION);return $}function q0(U){switch(U){case"s":case"sec":case"secs":case"second":case"seconds":return 1000;case"m":case"min":case"mins":case"minute":case"minutes":return 60000;case"h":case"hr":case"hrs":case"hour":case"hours":return 3600000;case"d":case"day":case"days":return 86400000;case"w":case"week":case"weeks":return 604800000;default:throw new Q(`Unknown time unit: ${U}`,z.VALIDATION)}}function C(U){if(U.at&&U.in)throw new Q("Pass only one of --at or --in",z.VALIDATION);if(U.at)return l(U.at).toISOString();if(U.in)return l(U.in).toISOString();throw new Q("Schedule time required. Pass --at <iso> or --in <duration> (e.g. 2h).",z.VALIDATION)}function IU(U){if(!U)return[];return U.split(",").map((D)=>D.trim()).filter(Boolean)}function yU(U){let D=Y(U,"text","content","c","t");if(!D)throw new Q("Missing --text",z.VALIDATION);return D}function s(U){let D=u(U,"accounts","account","a")??IU(Y(U,"accounts","account","a"));if(!D||D.length===0)throw new Q("Missing --accounts <id,id>",z.VALIDATION);return D}function t(U){return u(U,"media","media-urls")??IU(Y(U,"media","media-urls"))}function h(U){let D=Y(U,"meta","metadata","meta-json");if(!D)return;try{let O=JSON.parse(D);if(!O||typeof O!=="object"||Array.isArray(O))throw Error("metadata must be a JSON object");return O}catch(O){throw new Q(`Invalid --meta JSON: ${O.message}`,z.VALIDATION)}}async function BU(U,D,O,K){w(U),H(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/posts",{workspace:!0,query:{status:Y(O,"status","s"),socialAccountId:Y(O,"account","social-account"),groupId:Y(O,"group"),limit:P(O,"limit")??50,offset:P(O,"offset")??0}});R($,{...U,emptyMessage:"No posts yet. Schedule one with dun posts schedule."});return}case"get":{let $=K[0];if(!$)throw new Q("Usage: dun posts get <id>",z.USAGE);let A=await G.get(`/api/posts/${$}`,{workspace:!0});R(A,U);return}case"schedule":{let $=yU(O),A=s(O),V=C({at:Y(O,"at"),in:Y(O,"in")}),X=await G.post("/api/posts/schedule",{content:$,socialAccountIds:A,scheduledAt:V,mediaUrls:t(O),metadata:h(O)??{}},{workspace:!0});R(X,U);return}case"publish":case"publish-now":{let $=yU(O),A=s(O),V=await G.post("/api/posts/publish-now",{content:$,socialAccountIds:A,mediaUrls:t(O),metadata:h(O)??{},naturalPosting:F(O,"natural","natural-posting")},{workspace:!0});R(V,U);return}case"reschedule":{let $=K[0];if(!$)throw new Q("Usage: dun posts reschedule <id> --at|--in",z.USAGE);let V={scheduledAt:C({at:Y(O,"at"),in:Y(O,"in")})},X=Y(O,"text","content","c","t");if(X)V.content=X;let Z=t(O);if(Z.length)V.mediaUrls=Z;let J=h(O);if(J)V.metadata=J;if(!V.content){let q=await G.get(`/api/posts/${$}`,{workspace:!0});if(V.content=q.content,!V.metadata&&q.metadata)V.metadata=q.metadata;if(!V.mediaUrls&&q.mediaUrls)V.mediaUrls=q.mediaUrls}let N=await G.post(`/api/posts/${$}/reschedule`,V,{workspace:!0});R(N,U);return}case"cancel":{let $=K[0];if(!$)throw new Q("Usage: dun posts cancel <id>",z.USAGE);let A=await G.post(`/api/posts/${$}/cancel`,void 0,{workspace:!0});R(A??{id:$,status:"cancelled"},U);return}case"delete":case"rm":{let $=K[0];if(!$)throw new Q("Usage: dun posts delete <id> [--yes]",z.USAGE);if(!U.yes&&!F(O,"yes","y"))throw new Q("Refusing to delete without --yes (or CI/yes mode)",z.VALIDATION);let A=await G.delete(`/api/posts/${$}`,void 0,{workspace:!0});R(A??{id:$,deleted:!0},U);return}case"x-cap":case"xcap":{let $=await G.get("/api/posts/x-cap-usage",{workspace:!0});R($,U);return}case"schedule-thread":case"publish-thread":case"publish-thread-now":{let $=Y(O,"file","f"),A=Y(O,"account","accounts","a")??s(O)[0];if(!A)throw new Q("Missing --account <xAccountId>",z.VALIDATION);let V;if($){let Z=await Bun.file($).text(),J=JSON.parse(Z);if(Array.isArray(J))V=J;else if(J&&typeof J==="object"&&Array.isArray(J.tweets))V=J.tweets;else throw new Q("Thread file must be an array of tweets or { tweets: [] }",z.VALIDATION)}else{let Z=Y(O,"text","content");if(!Z)throw new Q("Provide --file thread.json (array of {content}) or informal --text with || separators is not enough alone for threads — use --file",z.VALIDATION);V=Z.split("||").map((J)=>({content:J.trim()})).filter((J)=>J.content)}if(V.length<2)throw new Q("Threads require at least 2 tweets",z.VALIDATION);if(D==="schedule-thread"){let Z=C({at:Y(O,"at"),in:Y(O,"in")}),J=await G.post("/api/posts/schedule-thread",{socialAccountId:A,tweets:V,scheduledAt:Z,metadata:h(O)??{}},{workspace:!0});R(J,U);return}let X=await G.post("/api/posts/publish-thread-now",{socialAccountId:A,tweets:V,metadata:h(O)??{}},{workspace:!0});R(X,U);return}default:throw new Q(`Unknown posts command "${D??""}". Try: list, get, schedule, publish, reschedule, cancel, delete, x-cap, schedule-thread, publish-thread`,z.USAGE)}}async function LU(U,D,O,K){w(U),H(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/drafts",{workspace:!0,query:{limit:P(O,"limit")??50,offset:P(O,"offset")??0}});R($,{...U,emptyMessage:'No drafts yet. Create one with dun drafts create --text "..."'});return}case"get":{let $=K[0];if(!$)throw new Q("Usage: dun drafts get <id>",z.USAGE);let A=await G.get(`/api/drafts/${$}`,{workspace:!0});R(A,U);return}case"create":{let $=Y(O,"text","content","c","t");if(!$)throw new Q("Missing --text",z.VALIDATION);let A=await G.post("/api/drafts",{content:$,name:Y(O,"name","n"),socialAccountIds:u(O,"accounts","account","a")??[],selectedPlatforms:u(O,"platforms","platform","p")??[],mediaUrls:u(O,"media")??[]},{workspace:!0});R(A,U);return}case"update":{let $=K[0];if(!$)throw new Q("Usage: dun drafts update <id> --text ...",z.USAGE);let A={id:$},V=Y(O,"text","content","c","t");if(V)A.content=V;let X=Y(O,"name","n");if(X!==void 0)A.name=X;let Z=u(O,"accounts","account","a");if(Z)A.socialAccountIds=Z;let J=u(O,"platforms","platform","p");if(J)A.selectedPlatforms=J;let N=u(O,"media");if(N)A.mediaUrls=N;let q=await G.patch(`/api/drafts/${$}`,A,{workspace:!0});R(q,U);return}case"delete":case"rm":{let $=K[0];if(!$)throw new Q("Usage: dun drafts delete <id> [--yes]",z.USAGE);if(!U.yes&&!F(O,"yes","y"))throw new Q("Refusing to delete without --yes",z.VALIDATION);let A=await G.delete(`/api/drafts/${$}`,void 0,{workspace:!0});R(A??{id:$,deleted:!0},U);return}default:throw new Q(`Unknown drafts command "${D??""}". Try: list, get, create, update, delete`,z.USAGE)}}import{basename as N0}from"node:path";function T0(U){let D=U.split(".").pop()?.toLowerCase()??"";return{jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",mp4:"video/mp4",mov:"video/quicktime",webm:"video/webm"}[D]??"application/octet-stream"}async function kU(U,D,O,K){w(U),H(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/media",{workspace:!0,query:{limit:P(O,"limit")??50,offset:P(O,"offset")??0}});R($,{...U,emptyMessage:"No media yet. Upload with dun media upload <file>."});return}case"get":{let $=K[0];if(!$)throw new Q("Usage: dun media get <id>",z.USAGE);let A=await G.get(`/api/media/${$}`,{workspace:!0});R(A,U);return}case"upload":{let $=K[0]||Y(O,"file","f");if(!$)throw new Q('Usage: dun media upload <file> [--alt "..."]',z.USAGE);let A=Bun.file($);if(!await A.exists())throw new Q(`File not found: ${$}`,z.VALIDATION);let X=N0($),Z=Y(O,"mime","type","content-type")||A.type||T0(X),J=A.size,N=Y(O,"alt","alt-text"),q=await G.post("/api/media/upload-url",{filename:X,mimeType:Z,fileSize:J},{workspace:!0});d(U.debug,`PUT ${q.uploadUrl}`,{storageKey:q.storageKey,fileSize:J});let I=await A.arrayBuffer(),k;try{k=await fetch(q.uploadUrl,{method:"PUT",headers:{"Content-Type":Z,"Content-Length":String(J)},body:I})}catch(L){throw new Q(`Upload PUT failed: ${L.message}`,z.NETWORK)}if(!k.ok){let L=await k.text().catch(()=>"");throw new Q(`Upload PUT failed with HTTP ${k.status}${L?`: ${L.slice(0,200)}`:""}`,z.NETWORK)}let B=await G.post("/api/media/complete",{storageKey:q.storageKey,originalFilename:X,mimeType:Z,fileSize:J,altText:N},{workspace:!0});R(B,U);return}case"delete":case"rm":{let $=K[0];if(!$)throw new Q("Usage: dun media delete <id> [--yes]",z.USAGE);if(!U.yes&&!F(O,"yes","y"))throw new Q("Refusing to delete without --yes",z.VALIDATION);let A=await G.delete(`/api/media/${$}`,void 0,{workspace:!0});R(A??{id:$,deleted:!0},U);return}default:throw new Q(`Unknown media command "${D??""}". Try: list, get, upload, delete`,z.USAGE)}}async function MU(U,D,O,K){w(U),H(U);let G=T(U),$=D[0];if($==="collections"||$==="collection"){let A=D[1]||"list";switch(A){case"list":case"ls":{let V=await G.get("/api/memory/collections",{workspace:!0});R(V,{...U,emptyMessage:'No memory collections yet. Create one with dun memory collections create --name "..."'});return}case"create":{let V=Y(O,"name","n");if(!V)throw new Q("Missing --name",z.VALIDATION);let X=await G.post("/api/memory/collections",{name:V,color:Y(O,"color")??"blue",isPrivate:F(O,"private")},{workspace:!0});R(X,U);return}case"get":{let V=D[2]||K[0];if(!V)throw new Q("Usage: dun memory collections get <id>",z.USAGE);let X=await G.get(`/api/memory/collections/${V}`,{workspace:!0});R(X,U);return}default:throw new Q(`Unknown memory collections command "${A}". Try: list, create, get`,z.USAGE)}}switch($){case"list":case"ls":{let A=Y(O,"collection","collection-id","c");if(!A)throw new Q("Missing --collection <id>",z.VALIDATION);let V=await G.get("/api/memory",{workspace:!0,query:{collectionId:A,limit:P(O,"limit")??100}});R(V,{...U,emptyMessage:"No memories in this collection."});return}case"save":case"add":{let A=Y(O,"collection","collection-id","c"),V=Y(O,"text","t");if(!A)throw new Q("Missing --collection <id>",z.VALIDATION);if(!V)throw new Q("Missing --text",z.VALIDATION);let X=await G.post("/api/memory",{collectionId:A,text:V},{workspace:!0});R(X,U);return}case"search":{let A=Y(O,"prompt","q","query","text"),V=u(O,"collections","collection","collection-ids","c")??[];if(!A)throw new Q("Missing --prompt",z.VALIDATION);if(V.length===0)throw new Q("Missing --collections <id,id>",z.VALIDATION);let X=await G.post("/api/memory/search",{prompt:A,collectionIds:V,topK:P(O,"top-k","topk","k")??5},{workspace:!0});R(X,U);return}case"delete":case"rm":{let A=D[1]||K[0],V=Y(O,"collection","collection-id","c");if(!A)throw new Q("Usage: dun memory delete <id> --collection <id>",z.USAGE);if(!V)throw new Q("Missing --collection <id>",z.VALIDATION);if(!U.yes&&!F(O,"yes","y"))throw new Q("Refusing to delete without --yes",z.VALIDATION);let X=await G.delete(`/api/memory/${A}`,{collectionId:V,memoryId:A},{workspace:!0});R(X??{id:A,deleted:!0},U);return}default:throw new Q(`Unknown memory command "${$??""}". Try: collections, list, save, search, delete`,z.USAGE)}}import{existsSync as W0}from"node:fs";import{mkdir as dU,writeFile as SU,readFile as hU}from"node:fs/promises";import{dirname as OU,join as W}from"node:path";import{homedir as DU}from"node:os";import{fileURLToPath as w0}from"node:url";var p=[{id:"claude",label:"Claude Code",aliases:["claude-code"],dest:(U)=>W(U,".claude","skills","dunsocial","SKILL.md")},{id:"codex",label:"Codex",dest:(U)=>W(U,".codex","skills","dunsocial","SKILL.md"),afterInstall:async(U)=>{return{agentsPath:await u0(U)}}},{id:"cursor",label:"Cursor",dest:(U)=>W(U,".cursor","skills","dunsocial","SKILL.md")},{id:"pi",label:"Pi",dest:(U)=>W(U,".pi","agent","skills","dunsocial","SKILL.md")},{id:"opencode",label:"OpenCode",dest:(U)=>W(U,".config","opencode","skills","dunsocial","SKILL.md")},{id:"agents",label:"Universal (.agents)",aliases:["universal"],dest:(U)=>W(U,".agents","skills","dunsocial","SKILL.md")},{id:"copilot",label:"GitHub Copilot",aliases:["github-copilot","github"],dest:(U)=>W(U,".copilot","skills","dunsocial","SKILL.md")},{id:"windsurf",label:"Windsurf",dest:(U)=>W(U,".codeium","windsurf","skills","dunsocial","SKILL.md")},{id:"goose",label:"Goose",dest:(U)=>W(U,".config","goose","skills","dunsocial","SKILL.md")},{id:"gemini",label:"Gemini CLI",aliases:["gemini-cli"],dest:(U)=>W(U,".gemini","skills","dunsocial","SKILL.md")},{id:"amp",label:"Amp",dest:(U)=>W(U,".config","agents","skills","dunsocial","SKILL.md")},{id:"grok",label:"Grok Build",dest:(U)=>W(U,".grok","skills","dunsocial","SKILL.md")},{id:"continue",label:"Continue",dest:(U)=>W(U,".continue","skills","dunsocial","SKILL.md")},{id:"antigravity",label:"Antigravity",dest:(U)=>W(U,".gemini","antigravity","skills","dunsocial","SKILL.md")}],a=new Map;for(let U of p){a.set(U.id,U);for(let D of U.aliases??[])a.set(D,U)}var jU=`Usage: dun skill install <${p.map((U)=>U.id).join("|")}|all>`;function UU(){let U=OU(w0(import.meta.url)),D=W("skills","dunsocial","SKILL.md"),O=[W(U,"..",D),W(U,"..","..",D)];return O.find(W0)??O[0]}function H0(){let U=DU();return p.map((D)=>({id:D.id,label:D.label,path:D.dest(U),aliases:D.aliases??[]}))}async function F0(){try{return await hU(UU(),"utf8")}catch{throw new Q(`Bundled skill not found at ${UU()}. Reinstall dunsocial.`,z.USAGE)}}async function P0(U,D){await dU(OU(U),{recursive:!0}),await SU(U,D,"utf8")}async function u0(U){let D=W(DU(),".codex","AGENTS.md");await dU(OU(D),{recursive:!0});let O="";try{O=await hU(D,"utf8")}catch{O=""}let K=["<!-- BEGIN DUNSOCIAL CLI SKILL -->","## DunSocial CLI","","Use the `dun` CLI for DunSocial scheduling from the shell. Prefer `--json` output.",`Skill details: ${U}`,"","Common flow:","1. `dun auth status --json`","2. `dun workspace list --json` / `dun workspace use <id>`","3. `dun accounts list --json`",'4. `dun posts schedule --text "..." --accounts <id> --in 1h --json`',"<!-- END DUNSOCIAL CLI SKILL -->"].join(`
232
- `),G="<!-- BEGIN DUNSOCIAL CLI SKILL -->",$="<!-- END DUNSOCIAL CLI SKILL -->",A,V=O.indexOf(G),X=O.indexOf($);if(V!==-1&&X!==-1&&X>V)A=O.slice(0,V)+K+O.slice(X+$.length);else if(O.trim())A=`${O.trimEnd()}
233
-
234
- ${K}
235
- `;else A=`${K}
236
- `;return await SU(D,A,"utf8"),D}async function bU(U,D){let O=U.dest(DU());await P0(O,D);let K=U.afterInstall?await U.afterInstall(O):{};return{target:U.id,path:O,message:`Installed DunSocial skill for ${U.label}. Restart or open a new session if needed.`,...K}}async function vU(U,D,O,K){switch(D){case"path":{R({path:UU()},U);return}case"list":{R({targets:H0(),hint:"Install with: dun skill install <target|all>"},U);return}case"install":{let G=(K[0]||"").toLowerCase();if(!G)throw new Q(jU,z.USAGE);let $=await F0();if(G==="all"){let X=[];for(let Z of p){let J=await bU(Z,$);X.push({target:J.target,path:J.path})}R({target:"all",count:X.length,installed:X,message:`Installed DunSocial skill for ${X.length} agents.`},U);return}let A=a.get(G);if(!A)throw new Q(`Unknown agent "${G}". ${jU}
237
- Try: dun skill list`,z.USAGE);let V=await bU(A,$);R(V,U);return}default:throw new Q(`Unknown skill command "${D??""}". Try: install, list, path`,z.USAGE)}}function mU(){if(e(process.stdout))NU(process.stdout,{tagline:!0});process.stdout.write(`${QU}
238
- `)}async function _0(U){let D=AU(U),{group:O,action:K,nest:G,flags:$,positionals:A}=D;if(f($)&&!O)return process.stdout.write(`${v}
239
- `),_.OK;if(O==="help"||!O&&n($)){let X=O==="help"?K||A[0]:void 0;if(X)process.stdout.write(`${g(X)}
240
- `);else mU();return _.OK}if(!O)return mU(),_.OK;if(n($))return process.stdout.write(`${g(O)}
241
- `),_.OK;let V=await VU({apiUrl:m($,"api-url","apiUrl"),token:m($,"token"),workspace:m($,"workspace","w"),json:y($,"json","j")?!0:void 0,debug:y($,"debug"),quiet:y($,"quiet","q"),yes:y($,"yes","y")});if(f($))return x({version:v},V),_.OK;try{switch(O){case"auth":await PU(V,K,$,A);break;case"workspace":case"workspaces":await uU(V,K,$,A);break;case"account":case"accounts":await _U(V,K??"list",$,A);break;case"post":case"posts":await BU(V,K,$,A);break;case"draft":case"drafts":await LU(V,K,$,A);break;case"media":await kU(V,K,$,A);break;case"memory":{let X=G?[G,K].filter(Boolean):K?[K]:[];await MU(V,X,$,A);break}case"skill":case"skills":await vU(V,K,$,A);break;case"version":x({version:v},V);break;default:throw new c(`Unknown command "${O}". Run dun --help`,_.USAGE)}return _.OK}catch(X){return zU(X,V)}}var y0=await _0(process.argv.slice(2));process.exit(y0);
247
+ ${A.verificationUrlComplete}
248
+
249
+ `),process.stderr.write(`Or go to ${A.verificationUrl} and enter code:
250
+ `),process.stderr.write(` ${A.userCode}
251
+
252
+ `),process.stderr.write(`Waiting for approval...
253
+ `)}else process.stderr.write(JSON.stringify({ok:!0,phase:"waiting",userCode:A.userCode,verificationUrlComplete:A.verificationUrlComplete})+`
254
+ `);if(G&&process.stdout.isTTY)try{await w0(A.verificationUrlComplete)}catch{}let $=Math.max(3,A.interval||5)*1000,V=Date.now()+(A.expiresIn||900)*1000;while(Date.now()<V){await R0($);let K=await O.post("/api/cli/auth/poll",{deviceCode:A.deviceCode},{auth:!1});if(K.status==="pending")continue;if(K.status==="expired")throw new Q("Login expired. Run dun auth login again.",z.AUTH);if(K.status==="denied")throw new Q("Login was denied in the browser.",z.AUTH);if(K.status==="approved"){let X=K.token,Z={...U,token:X},J=T(Z),q=await J.get("/api/user/profile"),Y={...U.config,apiUrl:U.apiUrl,token:X,defaultWorkspaceId:K.workspaceId||U.config.defaultWorkspaceId,defaultWorkspaceSlug:U.config.defaultWorkspaceSlug,defaultWorkspaceName:U.config.defaultWorkspaceName};if(K.workspaceId)try{let L=(await J.get("/api/workspaces")).find((k)=>k.id===K.workspaceId);if(L)Y.defaultWorkspaceId=L.id,Y.defaultWorkspaceSlug=L.slug,Y.defaultWorkspaceName=L.name}catch{}await b(Y);let H=Boolean(Y.defaultWorkspaceId);if(!U.json&&d(process.stdout)){R(E(H),U);return}R({user:{id:q.id,name:q.name,email:q.email},workspaceId:Y.defaultWorkspaceId??null,configPath:j(),method:"device",message:E(H)},U);return}}throw new Q("Login timed out. Run dun auth login again.",z.AUTH)}async function X0(U){if(await ZU(),!U.json&&d(process.stdout)){R("Logged out. Config cleared.",U);return}R({message:"Logged out. Config cleared.",configPath:j()},U)}async function Z0(U){N(U);let O=await T(U).get("/api/user/profile");R({id:O.id,name:O.name,email:O.email,image:O.image??null,apiUrl:U.apiUrl,workspaceId:U.workspaceId??null},U)}async function J0(U){let D=Boolean(U.token),O=null,A=null;if(D)try{O=await T(U).get("/api/user/profile")}catch(G){A=G instanceof Error?G.message:String(G)}R({authenticated:Boolean(O),tokenPresent:D,apiUrl:U.apiUrl,workspaceId:U.workspaceId??null,workspaceSlug:U.config.defaultWorkspaceSlug??null,workspaceName:U.config.defaultWorkspaceName??null,configPath:j(),user:O?{id:O.id,name:O.name,email:O.email}:null,error:A},U)}function R0(U){return new Promise((D)=>setTimeout(D,U))}async function w0(U){let D=process.platform,O=D==="darwin"?["open",U]:D==="win32"?["cmd","/c","start","",U]:["xdg-open",U];await Bun.spawn(O,{stdout:"ignore",stderr:"ignore"}).exited}async function FU(U,D,O,A){N(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/workspaces");R($.map((V)=>({id:V.id,name:V.name,slug:V.slug,role:V.role,timezone:V.timezone,plan:V.subscription?.planName??null,status:V.subscription?.status??null})),{...U,emptyMessage:"No workspaces yet."});return}case"use":{let $=A[0];if(!$)throw new Q("Usage: dun workspace use <id|slug>",z.USAGE);let K=(await G.get("/api/workspaces")).find((X)=>X.id===$||X.slug===$);if(!K)throw new Q(`Workspace not found: ${$}. Run dun workspace list`,z.NOT_FOUND);await b({...U.config,apiUrl:U.apiUrl,token:U.token,defaultWorkspaceId:K.id,defaultWorkspaceSlug:K.slug,defaultWorkspaceName:K.name}),R({id:K.id,name:K.name,slug:K.slug,role:K.role,message:`Default workspace set to ${K.name}`},U);return}case"current":{if(!U.workspaceId&&!U.config.defaultWorkspaceId)throw new Q("No workspace selected. Run dun workspace use <id|slug>",z.VALIDATION);let $=U.workspaceId||U.config.defaultWorkspaceId;try{let K=(await G.get("/api/workspaces")).find((X)=>X.id===$||X.slug===$);if(K){R({id:K.id,name:K.name,slug:K.slug,role:K.role,timezone:K.timezone},U);return}}catch{}R({id:$,name:U.config.defaultWorkspaceName??null,slug:U.config.defaultWorkspaceSlug??null},U);return}default:throw new Q(`Unknown workspace command "${D??""}". Try: list, use, current`,z.USAGE)}}async function _U(U,D,O,A){N(U),P(U);let G=T(U);switch(D){case void 0:case"list":case"ls":{let $=await G.get("/api/social-accounts",{workspace:!0}),V=w(O,"platform","p")?.toLowerCase(),K=V?$.filter((X)=>{let Z=(X.providerName||"").toLowerCase();return({x:["x","twitter"],twitter:["x","twitter"],linkedin:["linkedin"],"linkedin-business":["linkedin business","linkedin-business","linkedin_business"],bluesky:["bluesky"],threads:["threads"],reddit:["reddit"],pinterest:["pinterest"],instagram:["instagram"],youtube:["youtube"],tiktok:["tiktok"]}[V]??[V]).some((Y)=>Z.includes(Y))}):$;R(K.map((X)=>({id:X.id,providerName:X.providerName,username:X.username??null,displayName:X.displayName??null,isConnected:X.isConnected,expiresAt:X.expiresAt??null})),{...U,emptyMessage:V?`No ${V} accounts connected. Connect one in the DunSocial dashboard.`:"No accounts connected. Connect one in the DunSocial dashboard."});return}default:throw new Q(`Unknown accounts command "${D}". Try: list`,z.USAGE)}}var Y0=/^(\d+)\s*(s|sec|secs|second|seconds|m|min|mins|minute|minutes|h|hr|hrs|hour|hours|d|day|days|w|week|weeks)$/i;function l(U,D=new Date){let O=U.trim();if(!O)throw new Q("Empty time value",z.VALIDATION);let A=O.match(Y0);if(A){let V=Number(A[1]),K=A[2].toLowerCase(),X=q0(K)*V;return new Date(D.getTime()+X)}let G=O.match(/^in\s+(.+)$/i);if(G)return l(G[1],D);let $=new Date(O);if(Number.isNaN($.getTime()))throw new Q(`Invalid time "${U}". Use ISO-8601 (2026-06-01T09:00:00Z) or relative (2h, 30m, 1d).`,z.VALIDATION);return $}function q0(U){switch(U){case"s":case"sec":case"secs":case"second":case"seconds":return 1000;case"m":case"min":case"mins":case"minute":case"minutes":return 60000;case"h":case"hr":case"hrs":case"hour":case"hours":return 3600000;case"d":case"day":case"days":return 86400000;case"w":case"week":case"weeks":return 604800000;default:throw new Q(`Unknown time unit: ${U}`,z.VALIDATION)}}function C(U){if(U.at&&U.in)throw new Q("Pass only one of --at or --in",z.VALIDATION);if(U.at)return l(U.at).toISOString();if(U.in)return l(U.in).toISOString();throw new Q("Schedule time required. Pass --at <iso> or --in <duration> (e.g. 2h).",z.VALIDATION)}function yU(U){if(!U)return[];return U.split(",").map((D)=>D.trim()).filter(Boolean)}function IU(U){let D=w(U,"text","content","c","t");if(!D)throw new Q("Missing --text",z.VALIDATION);return D}function s(U){let D=_(U,"accounts","account","a")??yU(w(U,"accounts","account","a"));if(!D||D.length===0)throw new Q("Missing --accounts <id,id>",z.VALIDATION);return D}function t(U){return _(U,"media","media-urls")??yU(w(U,"media","media-urls"))}function h(U){let D=w(U,"meta","metadata","meta-json");if(!D)return;try{let O=JSON.parse(D);if(!O||typeof O!=="object"||Array.isArray(O))throw Error("metadata must be a JSON object");return O}catch(O){throw new Q(`Invalid --meta JSON: ${O.message}`,z.VALIDATION)}}async function BU(U,D,O,A){N(U),P(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/posts",{workspace:!0,query:{status:w(O,"status","s"),socialAccountId:w(O,"account","social-account"),groupId:w(O,"group"),limit:F(O,"limit")??50,offset:F(O,"offset")??0}});R($,{...U,emptyMessage:"No posts yet. Schedule one with dun posts schedule."});return}case"get":{let $=A[0];if(!$)throw new Q("Usage: dun posts get <id>",z.USAGE);let V=await G.get(`/api/posts/${$}`,{workspace:!0});R(V,U);return}case"schedule":{let $=IU(O),V=s(O),K=C({at:w(O,"at"),in:w(O,"in")}),X=await G.post("/api/posts/schedule",{content:$,socialAccountIds:V,scheduledAt:K,mediaUrls:t(O),metadata:h(O)??{}},{workspace:!0});R(X,U);return}case"publish":case"publish-now":{let $=IU(O),V=s(O),K=await G.post("/api/posts/publish-now",{content:$,socialAccountIds:V,mediaUrls:t(O),metadata:h(O)??{},naturalPosting:u(O,"natural","natural-posting")},{workspace:!0});R(K,U);return}case"reschedule":{let $=A[0];if(!$)throw new Q("Usage: dun posts reschedule <id> --at|--in",z.USAGE);let K={scheduledAt:C({at:w(O,"at"),in:w(O,"in")})},X=w(O,"text","content","c","t");if(X)K.content=X;let Z=t(O);if(Z.length)K.mediaUrls=Z;let J=h(O);if(J)K.metadata=J;if(!K.content){let Y=await G.get(`/api/posts/${$}`,{workspace:!0});if(K.content=Y.content,!K.metadata&&Y.metadata)K.metadata=Y.metadata;if(!K.mediaUrls&&Y.mediaUrls)K.mediaUrls=Y.mediaUrls}let q=await G.post(`/api/posts/${$}/reschedule`,K,{workspace:!0});R(q,U);return}case"cancel":{let $=A[0];if(!$)throw new Q("Usage: dun posts cancel <id>",z.USAGE);let V=await G.post(`/api/posts/${$}/cancel`,void 0,{workspace:!0});R(V??{id:$,status:"cancelled"},U);return}case"delete":case"rm":{let $=A[0];if(!$)throw new Q("Usage: dun posts delete <id> [--yes]",z.USAGE);if(!U.yes&&!u(O,"yes","y"))throw new Q("Refusing to delete without --yes (or CI/yes mode)",z.VALIDATION);let V=await G.delete(`/api/posts/${$}`,void 0,{workspace:!0});R(V??{id:$,deleted:!0},U);return}case"x-cap":case"xcap":{let $=await G.get("/api/posts/x-cap-usage",{workspace:!0});R($,U);return}case"schedule-thread":case"publish-thread":case"publish-thread-now":{let $=w(O,"file","f"),V=w(O,"account","accounts","a")??s(O)[0];if(!V)throw new Q("Missing --account <xAccountId>",z.VALIDATION);let K;if($){let Z=await Bun.file($).text(),J=JSON.parse(Z);if(Array.isArray(J))K=J;else if(J&&typeof J==="object"&&Array.isArray(J.tweets))K=J.tweets;else throw new Q("Thread file must be an array of tweets or { tweets: [] }",z.VALIDATION)}else{let Z=w(O,"text","content");if(!Z)throw new Q("Provide --file thread.json (array of {content}) or informal --text with || separators is not enough alone for threads - use --file",z.VALIDATION);K=Z.split("||").map((J)=>({content:J.trim()})).filter((J)=>J.content)}if(K.length<2)throw new Q("Threads require at least 2 tweets",z.VALIDATION);if(D==="schedule-thread"){let Z=C({at:w(O,"at"),in:w(O,"in")}),J=await G.post("/api/posts/schedule-thread",{socialAccountId:V,tweets:K,scheduledAt:Z,metadata:h(O)??{}},{workspace:!0});R(J,U);return}let X=await G.post("/api/posts/publish-thread-now",{socialAccountId:V,tweets:K,metadata:h(O)??{}},{workspace:!0});R(X,U);return}default:throw new Q(`Unknown posts command "${D??""}". Try: list, get, schedule, publish, reschedule, cancel, delete, x-cap, schedule-thread, publish-thread`,z.USAGE)}}async function LU(U,D,O,A){N(U),P(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/drafts",{workspace:!0,query:{limit:F(O,"limit")??50,offset:F(O,"offset")??0}});R($,{...U,emptyMessage:'No drafts yet. Create one with dun drafts create --text "..."'});return}case"get":{let $=A[0];if(!$)throw new Q("Usage: dun drafts get <id>",z.USAGE);let V=await G.get(`/api/drafts/${$}`,{workspace:!0});R(V,U);return}case"create":{let $=w(O,"text","content","c","t");if(!$)throw new Q("Missing --text",z.VALIDATION);let V=await G.post("/api/drafts",{content:$,name:w(O,"name","n"),socialAccountIds:_(O,"accounts","account","a")??[],selectedPlatforms:_(O,"platforms","platform","p")??[],mediaUrls:_(O,"media")??[]},{workspace:!0});R(V,U);return}case"update":{let $=A[0];if(!$)throw new Q("Usage: dun drafts update <id> --text ...",z.USAGE);let V={id:$},K=w(O,"text","content","c","t");if(K)V.content=K;let X=w(O,"name","n");if(X!==void 0)V.name=X;let Z=_(O,"accounts","account","a");if(Z)V.socialAccountIds=Z;let J=_(O,"platforms","platform","p");if(J)V.selectedPlatforms=J;let q=_(O,"media");if(q)V.mediaUrls=q;let Y=await G.patch(`/api/drafts/${$}`,V,{workspace:!0});R(Y,U);return}case"delete":case"rm":{let $=A[0];if(!$)throw new Q("Usage: dun drafts delete <id> [--yes]",z.USAGE);if(!U.yes&&!u(O,"yes","y"))throw new Q("Refusing to delete without --yes",z.VALIDATION);let V=await G.delete(`/api/drafts/${$}`,void 0,{workspace:!0});R(V??{id:$,deleted:!0},U);return}default:throw new Q(`Unknown drafts command "${D??""}". Try: list, get, create, update, delete`,z.USAGE)}}import{basename as T0}from"node:path";function N0(U){let D=U.split(".").pop()?.toLowerCase()??"";return{jpg:"image/jpeg",jpeg:"image/jpeg",png:"image/png",gif:"image/gif",webp:"image/webp",mp4:"video/mp4",mov:"video/quicktime",webm:"video/webm"}[D]??"application/octet-stream"}async function kU(U,D,O,A){N(U),P(U);let G=T(U);switch(D){case"list":case"ls":{let $=await G.get("/api/media",{workspace:!0,query:{limit:F(O,"limit")??50,offset:F(O,"offset")??0}});R($,{...U,emptyMessage:"No media yet. Upload with dun media upload <file>."});return}case"get":{let $=A[0];if(!$)throw new Q("Usage: dun media get <id>",z.USAGE);let V=await G.get(`/api/media/${$}`,{workspace:!0});R(V,U);return}case"upload":{let $=A[0]||w(O,"file","f");if(!$)throw new Q('Usage: dun media upload <file> [--alt "..."]',z.USAGE);let V=Bun.file($);if(!await V.exists())throw new Q(`File not found: ${$}`,z.VALIDATION);let X=T0($),Z=w(O,"mime","type","content-type")||V.type||N0(X),J=V.size,q=w(O,"alt","alt-text"),Y=await G.post("/api/media/upload-url",{filename:X,mimeType:Z,fileSize:J},{workspace:!0});S(U.debug,`PUT ${Y.uploadUrl}`,{storageKey:Y.storageKey,fileSize:J});let H=await V.arrayBuffer(),y;try{y=await fetch(Y.uploadUrl,{method:"PUT",headers:{"Content-Type":Z,"Content-Length":String(J)},body:H})}catch(k){throw new Q(`Upload PUT failed: ${k.message}`,z.NETWORK)}if(!y.ok){let k=await y.text().catch(()=>"");throw new Q(`Upload PUT failed with HTTP ${y.status}${k?`: ${k.slice(0,200)}`:""}`,z.NETWORK)}let L=await G.post("/api/media/complete",{storageKey:Y.storageKey,originalFilename:X,mimeType:Z,fileSize:J,altText:q},{workspace:!0});R(L,U);return}case"delete":case"rm":{let $=A[0];if(!$)throw new Q("Usage: dun media delete <id> [--yes]",z.USAGE);if(!U.yes&&!u(O,"yes","y"))throw new Q("Refusing to delete without --yes",z.VALIDATION);let V=await G.delete(`/api/media/${$}`,void 0,{workspace:!0});R(V??{id:$,deleted:!0},U);return}default:throw new Q(`Unknown media command "${D??""}". Try: list, get, upload, delete`,z.USAGE)}}async function MU(U,D,O,A){N(U),P(U);let G=T(U),$=D[0];if($==="collections"||$==="collection"){let V=D[1]||"list";switch(V){case"list":case"ls":{let K=await G.get("/api/memory/collections",{workspace:!0});R(K,{...U,emptyMessage:'No memory collections yet. Create one with dun memory collections create --name "..."'});return}case"create":{let K=w(O,"name","n");if(!K)throw new Q("Missing --name",z.VALIDATION);let X=await G.post("/api/memory/collections",{name:K,color:w(O,"color")??"blue",isPrivate:u(O,"private")},{workspace:!0});R(X,U);return}case"get":{let K=D[2]||A[0];if(!K)throw new Q("Usage: dun memory collections get <id>",z.USAGE);let X=await G.get(`/api/memory/collections/${K}`,{workspace:!0});R(X,U);return}default:throw new Q(`Unknown memory collections command "${V}". Try: list, create, get`,z.USAGE)}}switch($){case"list":case"ls":{let V=w(O,"collection","collection-id","c");if(!V)throw new Q("Missing --collection <id>",z.VALIDATION);let K=await G.get("/api/memory",{workspace:!0,query:{collectionId:V,limit:F(O,"limit")??100}});R(K,{...U,emptyMessage:"No memories in this collection."});return}case"save":case"add":{let V=w(O,"collection","collection-id","c"),K=w(O,"text","t");if(!V)throw new Q("Missing --collection <id>",z.VALIDATION);if(!K)throw new Q("Missing --text",z.VALIDATION);let X=await G.post("/api/memory",{collectionId:V,text:K},{workspace:!0});R(X,U);return}case"search":{let V=w(O,"prompt","q","query","text"),K=_(O,"collections","collection","collection-ids","c")??[];if(!V)throw new Q("Missing --prompt",z.VALIDATION);if(K.length===0)throw new Q("Missing --collections <id,id>",z.VALIDATION);let X=await G.post("/api/memory/search",{prompt:V,collectionIds:K,topK:F(O,"top-k","topk","k")??5},{workspace:!0});R(X,U);return}case"delete":case"rm":{let V=D[1]||A[0],K=w(O,"collection","collection-id","c");if(!V)throw new Q("Usage: dun memory delete <id> --collection <id>",z.USAGE);if(!K)throw new Q("Missing --collection <id>",z.VALIDATION);if(!U.yes&&!u(O,"yes","y"))throw new Q("Refusing to delete without --yes",z.VALIDATION);let X=await G.delete(`/api/memory/${V}`,{collectionId:K,memoryId:V},{workspace:!0});R(X??{id:V,deleted:!0},U);return}default:throw new Q(`Unknown memory command "${$??""}". Try: collections, list, save, search, delete`,z.USAGE)}}import{readFileSync as W0}from"node:fs";async function jU(U,D,O,A){N(U);let G=P(U),$=T(U),V=`/api/workspaces/${G}/ai-voice-settings`;switch(D){case"get":case"show":case void 0:{let K=await $.get(V,{workspace:!0});R(K??{personalization:null,hint:'No personalization set. Use dun personalization set --description "..."'},U);return}case"set":case"update":{let K=u(O,"clear"),X=w(O,"description","d"),Z=w(O,"file","f");if([K,Boolean(X),Boolean(Z)].filter(Boolean).length!==1)throw new Q("Use exactly one of --description, --file, or --clear",z.VALIDATION);let q;if(K)q=null;else if(Z){let H;try{H=W0(Z,"utf8").trim()}catch(y){throw new Q(`Could not read file: ${y.message}`,z.VALIDATION)}if(!H)throw new Q("File is empty",z.VALIDATION);q={description:H}}else{let H=X.trim();if(!H)throw new Q("--description cannot be empty",z.VALIDATION);q={description:H}}let Y=await $.patch(V,q,{workspace:!0});R(Y,U);return}default:throw new Q(`Unknown personalization command "${D}". Try: get, set`,z.USAGE)}}import{existsSync as P0}from"node:fs";import{mkdir as dU,writeFile as hU,readFile as vU}from"node:fs/promises";import{dirname as OU,join as W}from"node:path";import{homedir as DU}from"node:os";import{fileURLToPath as u0}from"node:url";var p=[{id:"claude",label:"Claude Code",aliases:["claude-code"],dest:(U)=>W(U,".claude","skills","dunsocial","SKILL.md")},{id:"codex",label:"Codex",dest:(U)=>W(U,".codex","skills","dunsocial","SKILL.md"),afterInstall:async(U)=>{return{agentsPath:await I0(U)}}},{id:"cursor",label:"Cursor",dest:(U)=>W(U,".cursor","skills","dunsocial","SKILL.md")},{id:"pi",label:"Pi",dest:(U)=>W(U,".pi","agent","skills","dunsocial","SKILL.md")},{id:"opencode",label:"OpenCode",dest:(U)=>W(U,".config","opencode","skills","dunsocial","SKILL.md")},{id:"agents",label:"Universal (.agents)",aliases:["universal"],dest:(U)=>W(U,".agents","skills","dunsocial","SKILL.md")},{id:"copilot",label:"GitHub Copilot",aliases:["github-copilot","github"],dest:(U)=>W(U,".copilot","skills","dunsocial","SKILL.md")},{id:"windsurf",label:"Windsurf",dest:(U)=>W(U,".codeium","windsurf","skills","dunsocial","SKILL.md")},{id:"goose",label:"Goose",dest:(U)=>W(U,".config","goose","skills","dunsocial","SKILL.md")},{id:"gemini",label:"Gemini CLI",aliases:["gemini-cli"],dest:(U)=>W(U,".gemini","skills","dunsocial","SKILL.md")},{id:"amp",label:"Amp",dest:(U)=>W(U,".config","agents","skills","dunsocial","SKILL.md")},{id:"grok",label:"Grok Build",dest:(U)=>W(U,".grok","skills","dunsocial","SKILL.md")},{id:"continue",label:"Continue",dest:(U)=>W(U,".continue","skills","dunsocial","SKILL.md")},{id:"antigravity",label:"Antigravity",dest:(U)=>W(U,".gemini","antigravity","skills","dunsocial","SKILL.md")}],a=new Map;for(let U of p){a.set(U.id,U);for(let D of U.aliases??[])a.set(D,U)}var bU=`Usage: dun skill install <${p.map((U)=>U.id).join("|")}|all>`;function UU(){let U=OU(u0(import.meta.url)),D=W("skills","dunsocial","SKILL.md"),O=[W(U,"..",D),W(U,"..","..",D)];return O.find(P0)??O[0]}function H0(){let U=DU();return p.map((D)=>({id:D.id,label:D.label,path:D.dest(U),aliases:D.aliases??[]}))}async function F0(){try{return await vU(UU(),"utf8")}catch{throw new Q(`Bundled skill not found at ${UU()}. Reinstall dunsocial.`,z.USAGE)}}async function _0(U,D){await dU(OU(U),{recursive:!0}),await hU(U,D,"utf8")}async function I0(U){let D=W(DU(),".codex","AGENTS.md");await dU(OU(D),{recursive:!0});let O="";try{O=await vU(D,"utf8")}catch{O=""}let A=["<!-- BEGIN DUNSOCIAL CLI SKILL -->","## DunSocial CLI","","Use the `dun` CLI for DunSocial scheduling from the shell. Prefer `--json` output.",`Skill details: ${U}`,"","Common flow:","1. `dun auth status --json`","2. `dun workspace list --json` / `dun workspace use <id>`","3. `dun accounts list --json`",'4. `dun posts schedule --text "..." --accounts <id> --in 1h --json`',"<!-- END DUNSOCIAL CLI SKILL -->"].join(`
255
+ `),G="<!-- BEGIN DUNSOCIAL CLI SKILL -->",$="<!-- END DUNSOCIAL CLI SKILL -->",V,K=O.indexOf(G),X=O.indexOf($);if(K!==-1&&X!==-1&&X>K)V=O.slice(0,K)+A+O.slice(X+$.length);else if(O.trim())V=`${O.trimEnd()}
256
+
257
+ ${A}
258
+ `;else V=`${A}
259
+ `;return await hU(D,V,"utf8"),D}async function SU(U,D){let O=U.dest(DU());await _0(O,D);let A=U.afterInstall?await U.afterInstall(O):{};return{target:U.id,path:O,message:`Installed DunSocial skill for ${U.label}. Restart or open a new session if needed.`,...A}}async function mU(U,D,O,A){switch(D){case"path":{R({path:UU()},U);return}case"list":{R({targets:H0(),hint:"Install with: dun skill install <target|all>"},U);return}case"install":{let G=(A[0]||"").toLowerCase();if(!G)throw new Q(bU,z.USAGE);let $=await F0();if(G==="all"){let X=[];for(let Z of p){let J=await SU(Z,$);X.push({target:J.target,path:J.path})}R({target:"all",count:X.length,installed:X,message:`Installed DunSocial skill for ${X.length} agents.`},U);return}let V=a.get(G);if(!V)throw new Q(`Unknown agent "${G}". ${bU}
260
+ Try: dun skill list`,z.USAGE);let K=await SU(V,$);R(K,U);return}default:throw new Q(`Unknown skill command "${D??""}". Try: install, list, path`,z.USAGE)}}function EU(){if(g(process.stdout))qU(process.stdout,{tagline:!0});process.stdout.write(`${QU}
261
+ `)}async function y0(U){let D=VU(U),{group:O,action:A,nest:G,flags:$,positionals:V}=D;if(f($)&&!O)return process.stdout.write(`${v}
262
+ `),I.OK;if(O==="help"||!O&&n($)){let X=O==="help"?A||V[0]:void 0;if(X)process.stdout.write(`${o(X)}
263
+ `);else EU();return I.OK}if(!O)return EU(),I.OK;if(n($))return process.stdout.write(`${o(O)}
264
+ `),I.OK;let K=await KU({apiUrl:m($,"api-url","apiUrl"),token:m($,"token"),workspace:m($,"workspace","w"),json:B($,"json","j")?!0:void 0,debug:B($,"debug"),quiet:B($,"quiet","q"),yes:B($,"yes","y")});if(f($))return x({version:v},K),I.OK;try{switch(O){case"auth":await HU(K,A,$,V);break;case"workspace":case"workspaces":await FU(K,A,$,V);break;case"account":case"accounts":await _U(K,A??"list",$,V);break;case"post":case"posts":await BU(K,A,$,V);break;case"draft":case"drafts":await LU(K,A,$,V);break;case"media":await kU(K,A,$,V);break;case"memory":{let X=G?[G,A].filter(Boolean):A?[A]:[];await MU(K,X,$,V);break}case"personalization":case"voice":await jU(K,A,$,V);break;case"skill":case"skills":await mU(K,A,$,V);break;case"version":x({version:v},K);break;default:throw new c(`Unknown command "${O}". Run dun --help`,I.USAGE)}return I.OK}catch(X){return zU(X,K)}}var B0=await y0(process.argv.slice(2));process.exit(B0);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "dunsocial",
3
- "version": "0.5.0",
3
+ "version": "0.7.0",
4
4
  "description": "DunSocial agent-first CLI — schedule and publish social posts from the terminal, CI, and coding agents",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -1,22 +1,20 @@
1
1
  #!/usr/bin/env node
2
2
  /**
3
3
  * Gated install tip — silent in CI / non-TTY / agent installs.
4
- * Keep in sync with apps/cli/src/brand.ts FIGlet Small art.
4
+ * Keep in sync with apps/cli/src/brand.ts OpenCode-style block art.
5
5
  */
6
6
  'use strict';
7
7
 
8
8
  const BANNER_FULL = [
9
- ' ___ ___ _ _ ',
10
- " | \\ _ _ _ _ / __| ___ __(_)__ _| |",
11
- " | |) | || | ' \\__ \\/ _ \\/ _| / _` | |",
12
- ' |___/ \\_,_|_||_|___/\\___/\\__|_\\__,_|_|'
9
+ '█▀▀▄ █ █▀▀▄ █▀▀▀ █▀▀█ █▀▀▀ ▀█▀ █▀▀█ █',
10
+ '█ █ █ █ ▀▀▀█ █ █ █ █ █▀▀█ █',
11
+ '▀▀▀▀ ▀▀▀▀ ▀ ▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀▀ ▀▀▀ ▀ ▀ ▀▀▀'
13
12
  ].join('\n');
14
13
 
15
14
  const BANNER_MINI = [
16
- ' _ ',
17
- ' __| |_ _ _ _ ',
18
- " / _` | || | ' \\ ",
19
- ' \\__,_|\\_,_|_||_|',
15
+ '█▀▀▄ █ █ █▀▀▄',
16
+ ' ',
17
+ '▀▀▀▀ ▀▀▀▀ ▀ ▀',
20
18
  '',
21
19
  ' DunSocial'
22
20
  ].join('\n');
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: dunsocial
3
- description: Schedule, publish, and manage DunSocial posts, drafts, media, and memory from the shell via the dun CLI (npm package dunsocial). Use when the user wants to post or schedule to X/LinkedIn/Bluesky/Threads/Reddit/Pinterest/Instagram/YouTube through DunSocial, manage drafts or media uploads, search workspace memory, or wire social publishing into CI/agent workflows.
3
+ description: Schedule, publish, and manage DunSocial posts, drafts, media, memory, and workspace personalization (AI voice) from the shell via the dun CLI (npm package dunsocial). Use when the user wants to post or schedule to X/LinkedIn/Bluesky/Threads/Reddit/Pinterest/Instagram/YouTube through DunSocial, manage drafts or media uploads, search workspace memory, get/set AI voice personalization, or wire social publishing into CI/agent workflows.
4
4
  ---
5
5
 
6
6
  # DunSocial CLI skill
@@ -47,7 +47,7 @@ PAT notes:
47
47
  1. `dun auth status --json`
48
48
  2. `dun workspace current --json` (or `list` + `use`)
49
49
  3. `dun accounts list --json` — capture social account **ids**
50
- 4. Act (schedule / publish / draft / media / memory)
50
+ 4. Act (schedule / publish / draft / media / memory / personalization)
51
51
 
52
52
  Do **not** invent account IDs. Do **not** treat `drafts create` as a successful publish.
53
53
 
@@ -88,6 +88,22 @@ dun memory search --prompt "…" --collections <id> --json
88
88
  dun posts schedule-thread --account acc_x --file ./thread.json --in 2h --json
89
89
  ```
90
90
 
91
+ ## Personalization (workspace AI voice)
92
+
93
+ Workspace-scoped writing style — same as web Settings → Personalization. Not per-user.
94
+
95
+ Agent recipe: get → rewrite → set.
96
+
97
+ ```bash
98
+ dun personalization get --json
99
+ dun personalization set --description "Concise, direct, no fluff. Short hooks." --json
100
+ # or:
101
+ dun personalization set --file ./voice.md --json
102
+ dun personalization set --clear --json
103
+ ```
104
+
105
+ `set` needs PAT scope `workspace:write` and owner/admin role. Alias: `dun voice …`.
106
+
91
107
  ## Destructive ops
92
108
 
93
109
  Require `--yes` (or `CI=1`) for `delete`.