From 1caa48735956fb322c13064c70615bbaef3cc265 Mon Sep 17 00:00:00 2001 From: MirSobZ Date: Fri, 10 Apr 2026 15:35:46 +0200 Subject: [PATCH] Chat Eksporter --- .ai_logs/chat-2026-04-10.md | 6 ++ .vscode/settings.json | 7 ++ .vscode/tasks.json | 15 ++++ .vscodeignore | 7 ++ README.md | 20 +++++ ai-chat-logger-1.0.3.vsix | Bin 0 -> 4310 bytes build.sh | 12 +++ extension.js | 155 ++++++++++++++++++++++++++++++++++++ package.json | 38 +++++++++ 9 files changed, 260 insertions(+) create mode 100644 .ai_logs/chat-2026-04-10.md create mode 100644 .vscode/settings.json create mode 100644 .vscode/tasks.json create mode 100644 .vscodeignore create mode 100644 README.md create mode 100644 ai-chat-logger-1.0.3.vsix create mode 100755 build.sh create mode 100644 extension.js create mode 100644 package.json diff --git a/.ai_logs/chat-2026-04-10.md b/.ai_logs/chat-2026-04-10.md new file mode 100644 index 0000000..fc5faf7 --- /dev/null +++ b/.ai_logs/chat-2026-04-10.md @@ -0,0 +1,6 @@ + + +--- +### [4/10/2026, 3:25:48 PM] + +3 diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..f9c34d9 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "chat.tools.terminal.autoApprove": { + "npm install": true, + "vsce": true, + "command": true + } +} \ No newline at end of file diff --git a/.vscode/tasks.json b/.vscode/tasks.json new file mode 100644 index 0000000..6c57ef7 --- /dev/null +++ b/.vscode/tasks.json @@ -0,0 +1,15 @@ +{ + "version": "2.0.0", + "tasks": [ + { + "label": "AI Chat Logger: Generuj VSIX", + "type": "shell", + "command": "./build.sh", + "group": { + "kind": "build", + "isDefault": true + }, + "problemMatcher": [] + } + ] +} \ No newline at end of file diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..1050516 --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,7 @@ +.vscode/** +.ai_logs/** +build.sh +node_modules/** +*.vsix +.git/** +.gitignore diff --git a/README.md b/README.md new file mode 100644 index 0000000..25ce2d0 --- /dev/null +++ b/README.md @@ -0,0 +1,20 @@ +# AI Chat Logger + +Minimalistyczny plugin zapisujący historię czatu Copilot Chat do plików Markdown. + +## Instalacja + +1. Skopiuj folder `ai-chat-logger` do dowolnego katalogu. +2. W VS Code uruchom: + - `F1 → Developer: Install Extension from Location` +3. Wskaż folder pluginu. + +## Konfiguracja + +W `settings.json`: + +```json +{ + "aiChatLogger.outputDir": ".ai_logs" +} +# ai-chat-logger diff --git a/ai-chat-logger-1.0.3.vsix b/ai-chat-logger-1.0.3.vsix new file mode 100644 index 0000000000000000000000000000000000000000..51cc249fa3c7b962b2365ca9a4cd559345aaea62 GIT binary patch literal 4310 zcmaKwc{r49-^YhBjL2?`EhHn9rI01rimX|)FN47t>&y_@WheX2DA^)p9oeQV4aT1A zTe4J^l&rV#xR3X(r~5hH=RL1KuIu{edz|O_`~J@3^F8&nfD|kM02mCA^^dh=oJ`L1 zA_oA*DFFZo002PvViE2bq=&nx4+iP$26sm~A~4v>bRCZwaoWhCFE1IeiRswPXGvBR zU#3AQJYe%7*Bi}>$~zgfF}kTg|7!P1g_*VN+EWEb!+2spikRhJ)#`&lpM4iy>2!F* zF1OO)FH|H5=&lF_uUO2W^^8H&Qk=^O50D_2zQRrI8>mkNK*LV z^Olf4frW$3IWR)`%E#J;!|0`U&jV;s`2yKzR6Ohfx1jvm?lKFtB!lFm{5)r@gY(;} zH!D)hS!nDr&qSUMmejsuO!mI<){w-v(%Q&g-ovh`3PK$W0yS`NBk#-pT>S}lA98=; zX_u=ce2F~Ts)0es=e74B)L5LAeJe-vo;~QCp*1KT3hAlS*C9}tmo=#3BS~FjK$%As z;>iKxk!4bP14P2Lal=eq|NCawUos2%Fk#s}coVZ(HVdWVL&f*oZTks^BmL;^cMmPY z8ld;t@G;2s`vx2AoQ>n&kQ~@B42v>tK*`PRZH#wuY)FkwA2@7C!Jb&TbD@1`@WixJ zE|_3tKv%A^mdZr^8kY!`19uJDIGYIIEIwr$Hd)$%?0o2Kbvu%eHhH|=&#|l4!6Wj% z9d}2eT$}kL|D=6WjQ)Y5igP#1)I>J4(wJ<)@$ES*SP{zo*#kgHxE8e~@`GyTRPi;8!zXgD zVWT`R0um@n^cUdoLY``AYaXN3gz(;)Uf(dJAfVnqOe(k&O?-1^PA4(|fcflgq3Yp& zW>c(GO(Mp1ui;> ztPTF$3J#g8F_5HzP{;Tmy{jMRJ(8r%_xGPb23L2(Cjx_+yMqr~HBgf441zWZKX}rgv;C+X=^=Ww2*Kp?9!W@k8@+Wr;E9zE$lXrPz5{CAI zyQ>6fEv&xx|F{dJ?L!4zOLfo|n@R0KYV7ksuM7+9A1|8$!?2K>*0 z!Dn45bJmq-Yy3U_(UoGJaC;Qo2_fo&@o-N!9B@DDO2Uy47-x9(t*ENI>I4}ua+7e8 z6Kdu)*^|PnuYv36R_MwvOdP&7$vfqi=gnT7O13?^uljDEHKcmdr3Mg+Es*YEBT7A{ zv&-<8y5muObWLSGIPAswYXSKxSfkLJsdb=~$%Y=BoauS09hMda^LNz+J)#Vua=e1C z(E0I8p)Y+toOes*y8J+{EBULz;L1$F+9*kH_H)uYQAkqNY9_KG!H#U7RVnB~`ir=Q zt<3E?%fUOKVYVy6CESiLC*<8^6`Rd6bmNRDRyYH@ryT?RIH^D@SE$Sd^2;wz;GNf1 z^o7NJO6Ddo>5LELqD0^G=fn)yP_~A6_=Q9nF!Q&HEv#%zMOYc8k|+EK#`C`V5iwHf z-XIr?OO+!J@U*5X(HJ*SCw(KkYGwE202smCe_I;OJEMIqL(9zmnq^7+PUkDw3AK!D zapK^6xqe%Ei=Y6o70ruMo88f&B{QUW6}y;bEL(Yy4{>ig0&*C96O}vN3LmkX9C!D4 zOB;UUZpJQm+)7i3`hgAKt?=fp4Vx(0DgRS6>73aOQCk0IMjP2v_P+9$e_NXh zykma_jz2T=7!dGBMHX6J9zC*6`hMLifhN(rXDMC;a zj>=bCQd@0+pkvn7r0QTV*$HcltiD6TGCRA69oP43Q{rRH?_6V^Kh)5wpV~C+$lfGdGDZr5otnsr1LHMTak)gAJ0O`{}`;XJh#ojg^TJUnynbEqmA8eI+#&ic7 zHJ(D*z8d`FE6P7|3L6nvtl-6J*sf|9OUFM^M7R&++dIh>or`JLDoNrf zL%*)e%-H7fikZG#`cS~0MW4>aUzH{lLk-f%(w#F;puAYZT-KYm^i|S`D8ZOa$2yoT z{alp9b^`2(#47hEwJ3$>SQeb`e?-QBqd6A@Sn964oR6EzB3tCTs4)tSAk$%k7E%6W1LZ2wFZSl{^*8jiB{@PJzL6`!w2bjaaat`RNEctXYeK&@H~!3 zLuODJb~(ptJ^4n60wQ(P+hW1dNYR3JN;^#Ma=OTch;IhHLZP>HSsCL)+S8?lSezTh z>R8Eal8UX1DUp0oQEz2lHGY0_MnPT&7&3JF&bA6HUQ0EM7^&~qcbGnwx%Q(J+1w>V z)s{k?%03kY&~#t$2nH5@+bayb60O0PvU^KT^R8Oflonm9S`LRRx|4{E4HH%QXnVar zo5>)1`JPPhaQRyZ7M-Cjpg)3Y#lM}92>&ST3JE_KW3kd0Xb#jc8M?86WsOp+nWPWP zNBIcWA%B|RLU6U@P=1ga65VQ>7n)8d-M&<(C+4uHo%X7)=-@&F471Y~7nW7K6Wzm? z>=ksBY*Ec5&3V!7E|K9}3L|29Q=Oz#b(^h&PTA~uM)Dr#)2?CA!E>Y2E*#4#p}$=C z7M%=pwgmeeLGbC+o+CgbTXrV*8mRFGLvOkWRkm9&P3PdAG|uQ$3!EN`yPMrH%}^Yd zC(5f@oJs2|2g30VIK@Lc0&SBm$L>y&GC&d!!XiD1uasBRQ>KMC4|r4Rjcdd&Bo?fJ zNnnfepB$kDD!6TVk}TO{^Xo#9G$mK#EqcXhNR#;+x~Q4l&S>_sua3{Ghq?ls=Vap% z;Xl_A4ZJbdcSrBPBc-k;ip&ZZ@u^ERx=s4h(jOsPY~aC$n&r)SnBpXfG2FhrD2ey+ z)r*#4YPr?|wF-We?3XS%&Tx`Kz?5bnZzTAn_`RclD)@?7cvb*K>3Xa_SIyBppw*UH z`ypsbjOfn#6X>Ccrp!p-t1I!M4x2ln(~8%7Re4^?1OfS!v|@;8H%lr`$`(bNaK+T? z>BI#b73jN{<%$-mc_C9<%O2mLw@CBp=}cT4;WHI~LrZKfMs}l5IApRf^0aq#WO?hv zt-RSvwiRceyu_|dk^6yn1GQ}GGp^9%5pnQDsPIK*d{sbW4Os=>V)(}etCy1GTb}|* zdaRPRck+Gx>_J6{JXJnuj;*ROdom6{6&DF6E|n3Wc4Q z#%O{kR4yP@;-a6EyS+B->fg2*pf0ngNZ~-Ds$BG%^7{IH)GP5V?wz8L+x_3Sm4=!N z$%RDCj6?(G>_D$K^uCio>s}u3Jz=(%L3Ak0+}@__Xk6W4@4b*=uovvC9si2(XpiHkSNo8STG}=4hj#Ns3rJ_l1V5?TOpspu z(mX=Q=1TcXqlO;{chcjOgiYfr-eN+{#;AC$`AdeC$e!SAj4EB5d&_O=$i%ay(&k)4YcUZ*F4 zzL;ire1o5pBChJilIwizeP`(q%O%{-yp_{}sxD9bVhdJUm31&J{+OsXvw2jP{bNjO z6rU%?0YQpv|MiOSvI~d2r;%wqlL_`oKv&xoyAHi^50H@*okFJ6v370mJ;3q1)uTq6pIXlwqi)cbU4nY3$~E@=NEOHkyO!_fJD>uT~H?P>?0UH=9R#1?$^C>qe#m z0Q18~brL67t9(C0;Bj?$m?G!5#6l{!lyCIaOE2Edn0*=R##LQ~&7tD@@h1b`tE(GW zt0%74o9qVykQqmV33B@B9phZWti+Xxs^k;nugQIOydtSi&s>vlRB9cAb5X~D@!Zr@ zQ?;_n)%DxL_AL$GnD2_tm5~F7y{Bg&LIut{DZv#mxy+vW^K1b{QFKx4ZOy&j`HClPQ%;-oe9CE_ z0J%YBE%jGmLFM*go!x;O*NH1!qmfJyyGm-!t5ZKRxVr0K(gh{XBl<2-t;w$L9awIT zcxp2p97L`u3E2xP$ZG&gAv*~zL;KtrEY0RV1U;9V#M$oK3FR4Saw64hMW1Hv_^YJJ za(oS>(qLaFRG-K+Y;^hAgSZ9Ic9?=enhLL$4kMRGTNxty*8#FxQ%-Sf;p%wP$bSe0FxfZ%OnIJlcMIaDIl1@{1H!QmCe}#<|ef zO1QY;)}7twA&lR@gf8}|pw}=tUeI(XYu5u^SFSR_-814NBUc3e_ssnZ_3K0b{`$SK z|488fsqvqY=-(Q}XKDWb2uQyKenpHw5%lkf@oN-?W}HFk?@9b4aQvz9H?02YmOnM> xLz6&%YW!9^i!Hx2{+q9U&7(e4i0)5~-!9YB0-fDe005w~NB7K(X!^hZ{smgPt#tqZ literal 0 HcmV?d00001 diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..6b1b84b --- /dev/null +++ b/build.sh @@ -0,0 +1,12 @@ +#!/bin/bash +# Skrypt do budowania rozszerzenia .vsix + +# Sprawdź czy vsce jest zainstalowane +if ! command -v vsce &> /dev/null +then + echo "vsce nie jest zainstalowane. Instalowanie globalnie..." + npm install -g @vscode/vsce +fi + +echo "Budowanie rozszerzenia..." +vsce package --no-git-tag-version --allow-missing-repository --skip-license --allow-star-activation diff --git a/extension.js b/extension.js new file mode 100644 index 0000000..f35ca43 --- /dev/null +++ b/extension.js @@ -0,0 +1,155 @@ +const vscode = require("vscode"); +const path = require("path"); + +function activate(context) { + const config = vscode.workspace.getConfiguration("aiChatLogger"); + const outputDir = config.get("outputDir") || ".ai_logs"; + const logFormat = config.get("format") || "markdown"; + + const workspaceFolder = vscode.workspace.workspaceFolders + ? vscode.workspace.workspaceFolders[0] + : null; + + if (!workspaceFolder) { + console.log("AI Chat Logger: brak workspace — plugin nie działa."); + return; + } + + const workspaceRoot = workspaceFolder.uri; + const fullOutputDir = vscode.Uri.joinPath(workspaceRoot, outputDir); + + // Inicjalizacja folderu logów + vscode.workspace.fs.createDirectory(fullOutputDir); + + // Rejestracja komendy ręcznego zapisu + let disposable = vscode.commands.registerCommand('ai-chat-logger.saveChat', async () => { + // Log all document schemes to debug + const allDocs = vscode.workspace.textDocuments; + const schemes = [...new Set(allDocs.map(d => d.uri.scheme))]; + console.log("All open documents:", allDocs.map(d => `${d.uri.scheme}:${d.uri.path}`)); + + // 1. Proba pobrania tekstu ze schowka (najskuteczniejsza metoda dla nowoczesnego Copilota) + try { + // Próbujemy skopiować całą treść czatu do schowka + await vscode.commands.executeCommand('workbench.action.chat.copyAll'); + const clipboardText = await vscode.env.clipboard.readText(); + + if (clipboardText && clipboardText.trim()) { + await saveToFile(clipboardText, "clipboard-chat"); + vscode.window.showInformationMessage("Czat zapisany pomyślnie na podstawie zawartości okna."); + return; + } + } catch (err) { + console.log("Metoda copyAll nie powiodła się, próbuję metodę dokumentową..."); + } + + // 2. Metoda dokumentowa dla starszych wersji lub specyficznych widoków + const chatDocs = vscode.workspace.textDocuments.filter(doc => + doc.uri.scheme === "vscode-chat" || + doc.uri.scheme === "vscode-chat-result" || + doc.uri.scheme === "chat-session-history" || + doc.uri.scheme === "comment" || + doc.uri.scheme === "vscode-chat-editor" || + doc.uri.scheme === "chat-editing-text-model" || + doc.uri.scheme === "chat-editing-snapshot-text-model"); + + if (chatDocs.length === 0) { + vscode.window.showErrorMessage(`Nie znaleziono otwartej sesji czatu AI w pamięci ani w schowku. (Dostępne schematy: ${schemes.join(", ")})`); + return; + } + + // Jeśli jest więcej niż jeden czat, spróbujmy wybrać najnowszy lub połączyć + let chosenDoc = chatDocs[0]; + if (chatDocs.length > 1) { + const items = chatDocs.map(d => ({ label: `Czat: ${d.uri.path} (${d.uri.scheme})`, doc: d })); + const selection = await vscode.window.showQuickPick(items, { placeHolder: "Wybierz sesję czatu do zapisu" }); + if (!selection) return; + chosenDoc = selection.doc; + } + + const content = chosenDoc.getText(); + + if (!content.trim()) { + vscode.window.showWarningMessage("Czat jest pusty."); + return; + } + + await saveToFile(content, "manual-chat"); + }); + + context.subscriptions.push(disposable); + + // Rejestracja zdarzenia zmiany dokumentu dla automatycznego logowania + const documentChangeDisposable = vscode.workspace.onDidChangeTextDocument(async event => { + const doc = event.document; + + // Lista obsługiwanych schematów chatu + const chatSchemes = ["vscode-chat", "vscode-chat-result", "chat-session-history", "chat-editing-text-model"]; + if (!chatSchemes.includes(doc.uri.scheme)) return; + + // Optymalizacja: zapisujemy tylko nowe zmiany (contentChanges) + if (event.contentChanges.length === 0) return; + + const date = new Date().toISOString().slice(0, 10); + const extension = logFormat === "markdown" ? "md" : "txt"; + const fileName = `chat-${date}.${extension}`; + const fileUri = vscode.Uri.joinPath(fullOutputDir, fileName); + + let newText = event.contentChanges + .map(change => change.text) + .join(""); + + if (!newText.trim()) return; + + const timestamp = new Date().toLocaleString(); + let entryText = ""; + + if (logFormat === "markdown") { + entryText = `\n\n---\n### [${timestamp}]\n\n${newText}\n`; + } else { + entryText = `\n\n---\n[${timestamp}]\n\n${newText}\n`; + } + + const entry = Buffer.from(entryText); + + try { + let existingContent = Buffer.from(""); + try { + existingContent = await vscode.workspace.fs.readFile(fileUri); + } catch (e) { + // Plik może nie istnieć, to normalne przy pierwszym wpisie dnia + } + + const combinedContent = Buffer.concat([existingContent, entry]); + await vscode.workspace.fs.writeFile(fileUri, combinedContent); + } catch (err) { + console.error("AI Chat Logger error:", err); + } + }); + + context.subscriptions.push(documentChangeDisposable); + + // Pomocnicza funkcja do zapisu (manualnego) + async function saveToFile(content, prefix) { + const date = new Date().toISOString().slice(0, 10); + const time = new Date().toTimeString().slice(0, 8).replace(/:/g, "-"); + const extension = logFormat === "markdown" ? "md" : "txt"; + const fileName = `${prefix}-${date}-${time}.${extension}`; + const fileUri = vscode.Uri.joinPath(fullOutputDir, fileName); + + const entry = Buffer.from(content); + + try { + await vscode.workspace.fs.writeFile(fileUri, entry); + vscode.window.showInformationMessage(`Czat zapisany w: ${outputDir}/${fileName}`); + } catch (err) { + vscode.window.showErrorMessage(`Błąd zapisu: ${err.message}`); + } + } + + console.log("AI Chat Logger aktywny."); +} + +function deactivate() {} + +module.exports = { activate, deactivate }; diff --git a/package.json b/package.json new file mode 100644 index 0000000..130a9f0 --- /dev/null +++ b/package.json @@ -0,0 +1,38 @@ +{ + "name": "ai-chat-logger", + "displayName": "AI Chat Logger", + "description": "Automatyczne zapisywanie historii czatu Copilot Chat do pliku.", + "version": "1.0.3", + "publisher": "local", + "engines": { + "vscode": "^1.80.0" + }, + "activationEvents": [], + "main": "./extension.js", + "contributes": { + "commands": [ + { + "command": "ai-chat-logger.saveChat", + "title": "AI Chat Logger: Zapisz Chat" + } + ], + "configuration": { + "type": "object", + "title": "AI Chat Logger", + "properties": { + "aiChatLogger.outputDir": { + "type": "string", + "default": ".ai_logs", + "description": "Folder, w którym będą zapisywane logi czatu." + }, + "aiChatLogger.format": { + "type": "string", + "enum": ["markdown", "plaintext"], + "default": "markdown", + "description": "Format zapisu logów czatu." + } + } + } + }, + "categories": ["Other"] +}