opencode-prompt-recorder 1.7.6 → 1.7.7

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
@@ -46,16 +46,16 @@ bun add -g opencode-prompt-recorder
46
46
  └── 2026/
47
47
  └── 06/
48
48
  └── 05/
49
- ├── 2606050943-什么是AI.md
50
- └── 2606051120-复制README.md
49
+ ├── 2606050943-什么是AI.txt
50
+ └── 2606051120-复制README.txt
51
51
  ```
52
52
 
53
53
  ## 文件名格式
54
54
 
55
- 每个提示词文件遵循以下格式:`yyMMddHHmm-{提示词主题}.md`
55
+ 每个提示词文件遵循以下格式:`yyMMddHHmm-{主题}.txt`
56
56
 
57
- - `yyMMddHHmm` - 首条消息的创建时间(年-月-日-时-分)
58
- - `{提示词主题}` - 首条提示词的前 40 个字符(已净化)
57
+ - `yyMMddHHmm` - 首条消息的创建时间(月-日-时-分)
58
+ - `{主题}` - 会话标题的前 40 个字符(已净化);标题不存在时回退到首条消息文本
59
59
 
60
60
  同一 session 的消息合并到同一个文件,文件内容格式:
61
61
 
@@ -81,7 +81,7 @@ bun add -g opencode-prompt-recorder
81
81
 
82
82
  ## 文件命名
83
83
 
84
- - 提示词主题从首条用户消息的第一行提取
84
+ - 主题优先从会话标题提取;标题不存在时回退到首条用户消息的第一行
85
85
  - 同一 session 的后续消息追加到同一文件,文件名不变
86
86
  - 文件名中的特殊字符会被自动清理(移除 `<>:"/\|?*` 及控制字符)
87
87
  - 文件名截断至最多 40 个字符
package/dist/index.js CHANGED
@@ -1,17 +1,17 @@
1
- import{mkdir as v,appendFile as N,writeFile as j,readFile as U}from"fs/promises";import{join as m,dirname as ne}from"path";import{fileURLToPath as re}from"url";import{readFile as G,rm as V}from"fs/promises";import{basename as h,dirname as y,join as b}from"path";import{fileURLToPath as z}from"url";var B="opencode-prompt-recorder";function K(n){console.error(`[prompt-recorder][autoUpdate] ${n}`)}function L(n,e){if(!e)return;let r=new AbortController,s=setTimeout(()=>r.abort(),1e4);X(r.signal).then(t=>{t.updated&&(K(`\u53D1\u73B0\u65B0\u7248\u672C: ${t.current} \u2192 ${t.latest}`),setTimeout(()=>{n.client.tui.showToast({body:{title:"Prompt Recorder \u66F4\u65B0",message:`${t.name} \u5DF2\u4ECE ${t.current} \u66F4\u65B0\u5230 ${t.latest}\uFF0C\u91CD\u542F OpenCode \u5B8C\u6210\u66F4\u65B0`,variant:"info"}})},5e3))}).catch(()=>{}).finally(()=>clearTimeout(s))}async function X(n){let e=await Z(B);if(!e)return{updated:!1};let r=await S(b(e,"package.json"));if(!r?.name||!r.version)return{updated:!1};let s=await ee(r.name,n);if(!s||!te(s,r.version))return{updated:!1};let t=await Y(e,r.name);if(!t)return{updated:!1};try{await V(t,{recursive:!0,force:!0})}catch{return{updated:!1,error:"remove_failed",name:r.name,current:r.version,latest:s}}return{updated:!0,name:r.name,current:r.version,latest:s}}async function Z(n){let e=y(z(import.meta.url));for(;;){if((await S(b(e,"package.json")))?.name===n)return h(e)==="dist"?y(e):e;let s=y(e);if(s===e)return;e=s}}async function Y(n,e){let r=y(n),s=h(r).startsWith("@")?y(r):r;if(h(s)!=="node_modules")return;let t=y(s),a=await S(b(t,"package.json")),c=q(t,e)??a?.dependencies?.[e];if(!(!c||!Q(c)))return t}function q(n,e){if(e.startsWith("@")){let[t,a]=e.split("/");if(!t||!a||h(y(n))!==t)return;let c=`${a}@`,l=h(n);return l.startsWith(c)?l.slice(c.length):void 0}let r=`${e}@`,s=h(n);return s.startsWith(r)?s.slice(r.length):void 0}function Q(n){let e=n.trim();return e?!!(e==="latest"||e==="*"||/^[~^]/.test(e)||/^(?:>=|>|<=|<)/.test(e)||/\s+(?:\|\||-|[<>=])\s+/.test(e)):!1}async function S(n){try{let e=JSON.parse(await G(n,"utf-8"));return e&&typeof e=="object"?e:void 0}catch{return}}async function ee(n,e){try{let r=await fetch(`https://registry.npmjs.org/${encodeURIComponent(n)}/latest`,{signal:e});if(!r.ok)return;let s=await r.json();if(!s||typeof s!="object")return;let t=s.version;return typeof t=="string"?t:void 0}catch{return}}function te(n,e){let r=E(n),s=E(e);if(!r||!s)return!1;for(let t=0;t<3;t++)if(r.parts[t]!==s.parts[t])return r.parts[t]>s.parts[t];if(!r.pre.length&&s.pre.length)return!0;if(r.pre.length&&!s.pre.length)return!1;for(let t=0;t<Math.max(r.pre.length,s.pre.length);t++){let a=r.pre[t],c=s.pre[t];if(a===void 0)return!1;if(c===void 0)return!0;if(a===c)continue;let l=/^\d+$/.test(a)?Number(a):void 0,p=/^\d+$/.test(c)?Number(c):void 0;return l!==void 0&&p!==void 0?l>p:l!==void 0?!1:p!==void 0?!0:a>c}return!1}function E(n){let e=n.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.+)?$/);if(e)return{parts:[Number(e[1]),Number(e[2]),Number(e[3])],pre:e[4]?.split(".")??[]}}var se=ne(re(import.meta.url));async function P(n,e){if(process.env.PROMPT_RECORDER_DEBUG!=="1"&&process.env.PROMPT_RECORDER_DEBUG!=="true")return;let s=`[${new Date().toISOString()}] ${e}
2
- `;try{let t=m(n,".agent","prompts-log");await v(t,{recursive:!0}),await N(m(t,"log.txt"),s)}catch(t){console.error("debugLog failed:",t)}}async function ie(){try{return JSON.parse(await U(m(se,"package.json"),"utf-8")).version}catch{return"unknown"}}var oe=/[<>:"/\\|?*\x00-\x1f]/g;function ae(n){return n.split(`
3
- `)[0].trim().replace(/<[^>]*>/g,"").replace(/\s+/g," ").trim().replace(oe,"").substring(0,40).trim()||"untitled"}function ce(n){let e=n.trimStart();return e.startsWith("<system-reminder>")||e.startsWith("<system>")}function ue(n){let e=n.getFullYear().toString(),r=String(n.getMonth()+1).padStart(2,"0"),s=String(n.getDate()).padStart(2,"0"),t=String(n.getHours()).padStart(2,"0"),a=String(n.getMinutes()).padStart(2,"0");return{yyyy:e,MM:r,dd:s,HH:t,mm:a}}var de=async n=>{L(n,!0);let{directory:e}=n,r=!1,s=new Map,t=new Set,a=new Set,c=1440*60*1e3,l=200,p=new Map;function H(){if(p.size<l)return;let d=Date.now();for(let[i,o]of p)d-o.time>c&&p.delete(i)}return{event:async({event:d})=>{if(d.type==="message.updated"){let i=d.properties.info,o=i?.role||i?.message?.role;i?.id&&o&&s.set(i.id,o)}if(d.type==="message.part.updated"){let i=d.properties.part;if(i?.type==="tool"&&i?.tool==="task"){let o=i.state?.metadata??i.metadata;if(o){let f=o.sessionId??o.sessionID;f&&(a.add(f),await P(e,`[prompt-recorder] tracked task session: ${f}`))}}if(i?.type==="text"&&i?.text){if(i.synthetic||i.ignored)return;let o=i.sessionID,f=i.messageID,u=i.text,g=s.get(f);if(g||(g=i.message?.role),g||(g=d.properties.info?.role),g||(g=d.properties.info?.message?.role),g==="user"&&u&&o){if(ce(u)){await P(e,`[prompt-recorder] filtered system-injected: sessionID=${o}`);return}let M=`${f}:${u}`;if(t.has(M))return;t.add(M),await P(e,`[prompt-recorder] event=${d.type}, role=${g}, sessionID=${o}, textLength=${u.length}, textPreview=${u.substring(0,50)}`);let T=new Date,{yyyy:w,MM:$,dd:k,HH:I,mm:x}=ue(T),R=m(e,".agent","prompts"),A=a.has(o)?m(R,"task",w,$,k):m(R,w,$,k);await v(A,{recursive:!0});let F=w.slice(-2),_=`============ ${w}-${$}-${k} ${I}:${x} ============`,D=p.get(o);if(D)D.time=Date.now(),await N(D.filepath,`
1
+ import{mkdir as x,appendFile as T,writeFile as N,readFile as H}from"fs/promises";import{join as g,dirname as se}from"path";import{fileURLToPath as ie}from"url";import{readFile as z,rm as B}from"fs/promises";import{basename as y,dirname as m,join as P}from"path";import{fileURLToPath as K}from"url";var X="opencode-prompt-recorder";function Z(n){console.error(`[prompt-recorder][autoUpdate] ${n}`)}var L=!1;function j(n,e){if(!e||L)return;L=!0;let r=new AbortController,s=setTimeout(()=>r.abort(),1e4);Y(r.signal).then(t=>{t.updated&&(Z(`\u53D1\u73B0\u65B0\u7248\u672C: ${t.current} \u2192 ${t.latest}`),setTimeout(()=>{n.client.tui.showToast({body:{title:"Prompt Recorder \u66F4\u65B0",message:`${t.name} \u5DF2\u4ECE ${t.current} \u66F4\u65B0\u5230 ${t.latest}\uFF0C\u91CD\u542F OpenCode \u5B8C\u6210\u66F4\u65B0`,variant:"info"}})},5e3))}).catch(()=>{}).finally(()=>clearTimeout(s))}async function Y(n){let e=await q(X);if(!e)return{updated:!1};let r=await M(P(e,"package.json"));if(!r?.name||!r.version)return{updated:!1};let s=await ne(r.name,n);if(!s||!re(s,r.version))return{updated:!1};let t=await Q(e,r.name);if(!t)return{updated:!1};try{await B(t,{recursive:!0,force:!0})}catch{return{updated:!1,error:"remove_failed",name:r.name,current:r.version,latest:s}}return{updated:!0,name:r.name,current:r.version,latest:s}}async function q(n){let e=m(K(import.meta.url));for(;;){if((await M(P(e,"package.json")))?.name===n)return y(e)==="dist"?m(e):e;let s=m(e);if(s===e)return;e=s}}async function Q(n,e){let r=m(n),s=y(r).startsWith("@")?m(r):r;if(y(s)!=="node_modules")return;let t=m(s),a=await M(P(t,"package.json")),c=ee(t,e)??a?.dependencies?.[e];if(!(!c||!te(c)))return t}function ee(n,e){if(e.startsWith("@")){let[t,a]=e.split("/");if(!t||!a||y(m(n))!==t)return;let c=`${a}@`,l=y(n);return l.startsWith(c)?l.slice(c.length):void 0}let r=`${e}@`,s=y(n);return s.startsWith(r)?s.slice(r.length):void 0}function te(n){let e=n.trim();return e?!!(e==="latest"||e==="*"||/^[~^]/.test(e)||/^(?:>=|>|<=|<)/.test(e)||/\s+(?:\|\||-|[<>=])\s+/.test(e)):!1}async function M(n){try{let e=JSON.parse(await z(n,"utf-8"));return e&&typeof e=="object"?e:void 0}catch{return}}async function ne(n,e){try{let r=await fetch(`https://registry.npmjs.org/${encodeURIComponent(n)}/latest`,{signal:e});if(!r.ok)return;let s=await r.json();if(!s||typeof s!="object")return;let t=s.version;return typeof t=="string"?t:void 0}catch{return}}function re(n,e){let r=U(n),s=U(e);if(!r||!s)return!1;for(let t=0;t<3;t++)if(r.parts[t]!==s.parts[t])return r.parts[t]>s.parts[t];if(!r.pre.length&&s.pre.length)return!0;if(r.pre.length&&!s.pre.length)return!1;for(let t=0;t<Math.max(r.pre.length,s.pre.length);t++){let a=r.pre[t],c=s.pre[t];if(a===void 0)return!1;if(c===void 0)return!0;if(a===c)continue;let l=/^\d+$/.test(a)?Number(a):void 0,h=/^\d+$/.test(c)?Number(c):void 0;return l!==void 0&&h!==void 0?l>h:l!==void 0?!1:h!==void 0?!0:a>c}return!1}function U(n){let e=n.match(/^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z.-]+))?(?:\+.+)?$/);if(e)return{parts:[Number(e[1]),Number(e[2]),Number(e[3])],pre:e[4]?.split(".")??[]}}var oe=se(ie(import.meta.url));async function v(n,e){if(process.env.PROMPT_RECORDER_DEBUG!=="1"&&process.env.PROMPT_RECORDER_DEBUG!=="true")return;let s=`[${new Date().toISOString()}] ${e}
2
+ `;try{let t=g(n,".agent","prompts-log");await x(t,{recursive:!0}),await T(g(t,"log.txt"),s)}catch(t){console.error("debugLog failed:",t)}}async function ae(){try{return JSON.parse(await H(g(oe,"package.json"),"utf-8")).version}catch{return"unknown"}}var ce=/[<>:"/\\|?*\x00-\x1f]/g;function ue(n){return n.split(`
3
+ `)[0].trim().replace(/<[^>]*>/g,"").replace(/\s+/g," ").trim().replace(ce,"").substring(0,40).trim()||"untitled"}function de(n){let e=n.trimStart();return e.startsWith("<system-reminder>")||e.startsWith("<system>")}function pe(n){let e=n.getFullYear().toString(),r=String(n.getMonth()+1).padStart(2,"0"),s=String(n.getDate()).padStart(2,"0"),t=String(n.getHours()).padStart(2,"0"),a=String(n.getMinutes()).padStart(2,"0");return{yyyy:e,MM:r,dd:s,HH:t,mm:a}}var fe=async n=>{j(n,!0);let{directory:e}=n,r=!1,s=new Map,t=new Map,a=new Set,c=new Set,l=1440*60*1e3,h=200,w=new Map;function F(){if(w.size<h)return;let u=Date.now();for(let[i,o]of w)u-o.time>l&&w.delete(i)}return{event:async({event:u})=>{if(u.type==="message.updated"){let i=u.properties.info,o=i?.role||i?.message?.role;i?.id&&o&&t.set(i.id,o)}if(u.type==="message.part.updated"){let i=u.properties.part;if(i?.type==="tool"&&i?.tool==="task"){let o=i.state?.metadata??i.metadata;if(o){let f=o.sessionId??o.sessionID;f&&(c.add(f),await v(e,`[prompt-recorder] tracked task session: ${f}`))}}if(i?.type==="text"&&i?.text){if(i.synthetic||i.ignored)return;let o=i.sessionID,f=i.messageID,d=i.text,p=t.get(f);if(p||(p=i.message?.role),p||(p=u.properties.info?.role),p||(p=u.properties.info?.message?.role),p==="user"&&d&&o){if(de(d)){await v(e,`[prompt-recorder] filtered system-injected: sessionID=${o}`);return}let b=`${f}:${d}`;if(a.has(b))return;a.add(b),await v(e,`[prompt-recorder] event=${u.type}, role=${p}, sessionID=${o}, textLength=${d.length}, textPreview=${d.substring(0,50)}`);let O=new Date,{yyyy:$,MM:k,dd:D,HH:I,mm:R}=pe(O),A=g(e,".agent","prompts"),_=c.has(o)?g(A,"task",$,k,D):g(A,$,k,D);await x(_,{recursive:!0});let J=$.slice(-2),C=`============ ${$}-${k}-${D} ${I}:${R} ============`,S=w.get(o);if(S)S.time=Date.now(),await T(S.filepath,`
4
4
 
5
- ${_}
5
+ ${C}
6
6
 
7
- ${u}`);else{let O=ae(u),J=`${F}${$}${k}${I}${x}-${O}.md`,C=m(A,J),W=`============ SessionID: ${o} ============`;await j(C,`${W}
7
+ ${d}`);else{let W=ue(s.get(o)??d),G=`${J}${k}${D}${I}${R}-${W}.txt`,E=g(_,G),V=`============ SessionID: ${o} ============`;await N(E,`${V}
8
8
 
9
- ${_}
9
+ ${C}
10
10
 
11
- ${u}`),p.set(o,{filepath:C,time:Date.now()}),H()}}}}if(d.type==="session.updated"&&!r)try{let i=await ie(),o=m(e,".agent"),f=m(o,"opencode-prompt-recorder-readme.txt"),u=`# OpenCode Prompt Recorder
11
+ ${d}`),w.set(o,{filepath:E,time:Date.now()}),F()}}}}if(u.type==="session.created"){let i=u.properties.info;i?.id&&i?.title&&s.set(i.id,i.title)}if(u.type==="session.updated"){let i=u.properties.info;if(i?.id&&i?.title&&s.set(i.id,i.title),!r)try{let o=await ae(),f=g(e,".agent"),d=g(f,"opencode-prompt-recorder-readme.txt"),p=`# OpenCode Prompt Recorder
12
12
 
13
13
  \u81EA\u52A8\u8BB0\u5F55\u7528\u6237\u63D0\u793A\u8BCD\u5230 .agent/prompts \u76EE\u5F55\u7684\u63D2\u4EF6\u3002
14
14
 
15
- \u7248\u672C\uFF1A${i}
15
+ \u7248\u672C\uFF1A${o}
16
16
  \u4F5C\u8005\uFF1Aanarckk
17
- \u9879\u76EE\u5730\u5740\uFF1Ahttps://github.com/anarckk/opencode-prompt-recorder`;try{if(await U(f,"utf-8")===u){r=!0;return}}catch{}await v(o,{recursive:!0}),await j(f,u),r=!0}catch{}}}},ke=de;export{de as OpenCodePromptRecorder,ke as default};
17
+ \u9879\u76EE\u5730\u5740\uFF1Ahttps://github.com/anarckk/opencode-prompt-recorder`;try{if(await H(d,"utf-8")===p){r=!0;return}}catch{}await x(f,{recursive:!0}),await N(d,p),r=!0}catch{}}}}},be=fe;export{fe as OpenCodePromptRecorder,be as default};
package/dist/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-prompt-recorder",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "description": "OpenCode plugin for recording user prompts. Automatically saves user messages to a local file system with organized directory structure.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "opencode-prompt-recorder",
3
- "version": "1.7.6",
3
+ "version": "1.7.7",
4
4
  "description": "OpenCode plugin for recording user prompts. Automatically saves user messages to a local file system with organized directory structure.",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",