Initial commit to new repository

This commit is contained in:
2026-04-03 18:23:52 +09:00
commit deffb33cf9
5248 changed files with 267762 additions and 0 deletions

BIN
.DS_Store vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
.vs/AxCommander/v18/.suo Normal file

Binary file not shown.

View File

@@ -0,0 +1,34 @@
{
"Version": 1,
"WorkspaceRootPath": "E:\\AX Commander\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\admin\\AppData\\Local\\Temp\\.vsdbgsrc\\47857252c2f7918acd7cde28b121406cd435acaad3354ed659f220603fb46572\\XamlReader.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "XamlReader.cs",
"DocumentMoniker": "C:\\Users\\admin\\AppData\\Local\\Temp\\.vsdbgsrc\\47857252c2f7918acd7cde28b121406cd435acaad3354ed659f220603fb46572\\XamlReader.cs",
"ToolTip": "C:\\Users\\admin\\AppData\\Local\\Temp\\.vsdbgsrc\\47857252c2f7918acd7cde28b121406cd435acaad3354ed659f220603fb46572\\XamlReader.cs",
"ViewState": "AgIAAMIBAAAAAAAAAAAUwNkBAAACAAAAAQAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-26T21:45:20.636Z",
"EditorCaption": ""
}
]
}
]
}
]
}

View File

@@ -0,0 +1,34 @@
{
"Version": 1,
"WorkspaceRootPath": "E:\\AX Commander\\",
"Documents": [
{
"AbsoluteMoniker": "D:0:0:{A2FE74E1-B743-11D0-AE1A-00A0C90FFFC3}|\u003CMiscFiles\u003E|C:\\Users\\admin\\AppData\\Local\\Temp\\.vsdbgsrc\\47857252c2f7918acd7cde28b121406cd435acaad3354ed659f220603fb46572\\XamlReader.cs||{A6C744A8-0E4A-4FC6-886A-064283054674}"
}
],
"DocumentGroupContainers": [
{
"Orientation": 0,
"VerticalTabListWidth": 256,
"DocumentGroups": [
{
"DockedWidth": 200,
"SelectedChildIndex": 0,
"Children": [
{
"$type": "Document",
"DocumentIndex": 0,
"Title": "XamlReader.cs",
"DocumentMoniker": "C:\\Users\\admin\\AppData\\Local\\Temp\\.vsdbgsrc\\47857252c2f7918acd7cde28b121406cd435acaad3354ed659f220603fb46572\\XamlReader.cs",
"ToolTip": "C:\\Users\\admin\\AppData\\Local\\Temp\\.vsdbgsrc\\47857252c2f7918acd7cde28b121406cd435acaad3354ed659f220603fb46572\\XamlReader.cs",
"ViewState": "AgIAAMIBAAAAAAAAAAAYwNkBAAACAAAAAQAAAA==",
"Icon": "ae27a6b0-e345-4288-96df-5eaf394ee369.000738|",
"WhenOpened": "2026-03-26T21:45:20.636Z",
"EditorCaption": ""
}
]
}
]
}
]
}

Binary file not shown.

Binary file not shown.

Binary file not shown.

540
AXCommander_SRS_draft.rtf Normal file
View File

@@ -0,0 +1,540 @@
{\rtf1\ansi\ansicpg949\cocoartf2868
\cocoatextscaling0\cocoaplatform0{\fonttbl\f0\fswiss\fcharset0 Helvetica;\f1\fnil\fcharset129 AppleSDGothicNeo-Regular;\f2\fnil\fcharset0 LucidaGrande;
}
{\colortbl;\red255\green255\blue255;}
{\*\expandedcolortbl;;}
\paperw11900\paperh16840\margl1440\margr1440\vieww20200\viewh15100\viewkind0
\pard\tx720\tx1440\tx2160\tx2880\tx3600\tx4320\tx5040\tx5760\tx6480\tx7200\tx7920\tx8640\pardirnatural\partightenfactor0
\f0\fs24 \cf0 1.
\f1 \'bd\'c3\'bd\'ba\'c5\'db
\f0
\f1 \'c7\'d9\'bd\'c9
\f0
\f1 \'be\'c6\'c5\'b0\'c5\'d8\'c3\'b3
\f0 \
\f1 \'c1\'d6\'bf\'e4
\f0
\f1 \'b8\'f0\'b5\'e2
\f0
\f1 \'b1\'b8\'bc\'ba\
* \'c0\'a9\'b5\'b5\'bf\'ec os\'bf\'a1\'bc\'ad \'b5\'bf\'c0\'db\'c7\'cf\'b4\'c2 \'c7\'c1\'b7\'ce\'b1\'d7\'b7\'a5\'c0\'b8\'b7\'ce \'c1\'a4\'c0\'c7
\f0 \
* Input Listener (Global Hook):
\f1 \'be\'ee\'b6\'b2
\f0
\f1 \'c7\'c1\'b7\'ce\'b1\'d7\'b7\'a5
\f0
\f1 \'c0\'a7\'bf\'a1
\f0
\f1 \'c0\'d6\'b5\'e7
\f0 Alt + Space
\f1 \'bf\'cd
\f0
\f1 \'b0\'b0\'c0\'ba
\f0
\f1 \'b4\'dc\'c3\'e0\'c5\'b0\'b8\'a6
\f0
\f1 \'b0\'a8\'c1\'f6\'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
* Context Manager:
\f1 \'c7\'f6\'c0\'e7
\f0
\f1 \'bf\'ad\'b7\'c1
\f0
\f1 \'c0\'d6\'b4\'c2
\f0
\f1 \'c0\'a9\'b5\'b5\'bf\'ec\'b5\'e9\'c0\'c7
\f0
\f1 \'c7\'da\'b5\'e9
\f0 (HWND),
\f1 \'c7\'c1\'b7\'ce\'bc\'bc\'bd\'ba
\f0
\f1 \'b0\'e6\'b7\'ce
\f0 ,
\f1 \'c3\'a2
\f0
\f1 \'c0\'a7\'c4\'a1
\f0 (Rect)
\f1 \'b8\'a6
\f0
\f1 \'bd\'ba\'b3\'c0\'bc\'a6\'c0\'b8\'b7\'ce
\f0
\f1 \'c4\'b8\'c3\'b3\'c7\'cf\'b0\'ed
\f0
\f1 \'ba\'b9\'bf\'f8\'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
* Command Resolver:
\f1 \'c0\'d4\'b7\'c2\'b5\'c8
\f0
\f1 \'b8\'ed\'b7\'c9\'be\'ee
\f0 (
\f1 \'bf\'b9
\f0 : @nas, !dev)
\f1 \'b8\'a6
\f0
\f1 \'c7\'d8\'bc\'ae\'c7\'cf\'bf\'a9
\f0
\f1 \'b9\'cc\'b8\'ae
\f0
\f1 \'c1\'a4\'c0\'c7\'b5\'c8
\f0
\f1 \'be\'d7\'bc\'c7
\f0 (
\f1 \'c6\'fa\'b4\'f5
\f0
\f1 \'bf\'ad\'b1\'e2
\f0 ,
\f1 \'b9\'e8\'c4\'a1
\f0
\f1 \'bd\'c7\'c7\'e0
\f0 ,
\f1 \'c0\'a9\'b5\'b5\'bf\'ec
\f0
\f1 \'c1\'a4\'b7\'c4
\f0 )
\f1 \'c0\'bb
\f0
\f1 \'b8\'c5\'c7\'ce\'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
* Fuzzy Engine:
\f1 \'c6\'c4\'c0\'cf\'b8\'ed\'c0\'cc\'b3\'aa
\f0
\f1 \'c5\'b0\'bf\'f6\'b5\'e5\'c0\'c7
\f0
\f1 \'c0\'cf\'ba\'ce\'b8\'b8
\f0
\f1 \'c0\'d4\'b7\'c2\'c7\'d8\'b5\'b5
\f0
\f1 \'b0\'a1\'c0\'e5
\f0
\f1 \'c0\'af\'bb\'e7\'c7\'d1
\f0
\f1 \'c7\'d7\'b8\'f1\'c0\'bb
\f0
\f1 \'c3\'ca\'b0\'ed\'bc\'d3\'c0\'b8\'b7\'ce
\f0
\f1 \'c3\'a3\'be\'c6\'c1\'dd\'b4\'cf\'b4\'d9
\f0 .\
* max os\uc0\u51032 \u50508 \u54532 \u47112 \u46300 \u50752 \u50976 \u49324 \u54620 \u44592 \u45733 \u51012 \u50952 \u46020 \u50864 os\u50640 \u44396 \u52629 \
2.
\f1 \'c1\'d6\'bf\'e4
\f0
\f1 \'b1\'e2\'b4\'c9
\f0
\f1 \'bb\'f3\'bc\'bc
\f0 (Feature List)\
A.
\f1 \'bf\'f6\'c5\'a9\'bd\'ba\'c6\'e4\'c0\'cc\'bd\'ba
\f0
\f1 \'bd\'ba\'b3\'c0\'bc\'a6
\f0 &
\f1 \'bd\'c3\'c7\'c1\'c6\'ae
\f0 (The Shifter)\
\f1 \'b4\'dc\'bc\'f8\'c8\'f7
\f0
\f1 \'c7\'c1\'b7\'ce\'b1\'d7\'b7\'a5\'c0\'bb
\f0
\f1 \'b6\'e7\'bf\'ec\'b4\'c2
\f0
\f1 \'b0\'cd\'c0\'cc
\f0
\f1 \'be\'c6\'b4\'cf\'b6\'f3
\f0 , '
\f1 \'be\'ee\'b6\'b2
\f0
\f1 \'c0\'a7\'c4\'a1\'bf\'a1
\f0
\f1 \'be\'ee\'b6\'b2
\f0
\f1 \'c5\'a9\'b1\'e2\'b7\'ce
\f0 '
\f1 \'b6\'b0
\f0
\f1 \'c0\'d6\'b4\'c2\'c1\'f6\'b1\'ee\'c1\'f6
\f0
\f1 \'b0\'fc\'b8\'ae\'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
* Snapshot Capture:
\f1 \'c7\'f6\'c0\'e7
\f0
\f1 \'c8\'ad\'b8\'e9\'bf\'a1
\f0
\f1 \'b6\'b0
\f0
\f1 \'c0\'d6\'b4\'c2
\f0
\f1 \'b8\'f0\'b5\'e7
\f0
\f1 \'be\'f7\'b9\'ab\'bf\'eb
\f0
\f1 \'c3\'a2
\f0
\f1 \'b9\'e8\'c4\'a1\'b8\'a6
\f0 '
\f1 \'c7\'c1\'b7\'ce\'c7\'ca
\f0 '
\f1 \'b7\'ce
\f0
\f1 \'c0\'fa\'c0\'e5\'c7\'d5\'b4\'cf\'b4\'d9
\f0 . (
\f1 \'bf\'b9
\f0 :
\f1 \'bf\'de\'c2\'ca\'bf\'a3
\f0 VS Code,
\f1 \'bf\'c0\'b8\'a5\'c2\'ca
\f0
\f1 \'bb\'f3\'b4\'dc\'bf\'a3
\f0
\f1 \'b1\'e2\'c8\'b9\'bc\'ad
\f0 PDF,
\f1 \'c7\'cf\'b4\'dc\'bf\'a3
\f0 SQL
\f1 \'c5\'f8
\f0 )\
* Instant Restore: !dev
\f1 \'b8\'ed\'b7\'c9
\f0
\f1 \'bd\'c3
\f0 ,
\f1 \'c0\'fa\'c0\'e5\'b5\'c8
\f0
\f1 \'c7\'c1\'b7\'ce\'c7\'ca\'c0\'bb
\f0
\f1 \'ba\'d2\'b7\'af\'bf\'cd
\f0
\f1 \'c3\'a2\'b5\'e9\'c0\'bb
\f0
\f1 \'b0\'ad\'c1\'a6\'b7\'ce
\f0
\f1 \'c0\'e7\'b9\'e8\'c4\'a1\'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
* Multi-Monitor Support:
\f1 \'b5\'e0\'be\'f3
\f0
\f1 \'b8\'f0\'b4\'cf\'c5\'cd
\f0
\f1 \'c8\'af\'b0\'e6\'bf\'a1\'bc\'ad\'b5\'b5
\f0
\f1 \'b0\'a2
\f0
\f1 \'b8\'f0\'b4\'cf\'c5\'cd\'ba\'b0
\f0
\f1 \'c3\'a2
\f0
\f1 \'b9\'e8\'c4\'a1\'b8\'a6
\f0
\f1 \'c1\'a4\'c8\'ae\'c8\'f7
\f0
\f1 \'b1\'e2\'be\'ef\'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
B.
\f1 \'bd\'c3\'b8\'c7\'c6\'bd
\f0
\f1 \'c4\'bf\'b8\'c7\'b5\'e5
\f0
\f1 \'b7\'b1\'c3\'b3
\f0 (The Alfred)\
\f1 \'c5\'b0\'ba\'b8\'b5\'e5\'b8\'b8\'c0\'b8\'b7\'ce
\f0
\f1 \'b8\'b6\'bf\'ec\'bd\'ba
\f0
\f1 \'c5\'ac\'b8\'af
\f0
\f1 \'bc\'f6\'bd\'ca
\f0
\f1 \'b9\'f8\'c0\'c7
\f0
\f1 \'b0\'a1\'c4\'a1\'b8\'a6
\f0
\f1 \'b8\'b8\'b5\'e9\'be\'ee\'b3\'c0\'b4\'cf\'b4\'d9
\f0 .\
* Smart Aliases:
\f1 \'b1\'e4
\f0
\f1 \'b0\'e6\'b7\'ce\'b3\'aa
\f0
\f1 \'ba\'b9\'c0\'e2\'c7\'d1
\f0
\f1 \'b8\'ed\'b7\'c9\'c0\'bb
\f0
\f1 \'c2\'aa\'c0\'ba
\f0
\f1 \'ba\'b0\'c4\'aa\'c0\'b8\'b7\'ce
\f0
\f1 \'b5\'ee\'b7\'cf\'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
* @blog
\f2 \uc0\u8594
\f0 swarchitect.net
\f1 \'b0\'fc\'b8\'ae\'c0\'da
\f0
\f1 \'c6\'e4\'c0\'cc\'c1\'f6
\f0
\f1 \'bf\'c0\'c7\'c2
\f0 \
* #jira
\f2 \uc0\u8594
\f0
\f1 \'b3\'bb\'b0\'a1
\f0
\f1 \'b4\'e3\'b4\'e7\'c7\'d1
\f0
\f1 \'c3\'d6\'bd\'c5
\f0
\f1 \'c6\'bc\'c4\'cf
\f0
\f1 \'c6\'e4\'c0\'cc\'c1\'f6\'b7\'ce
\f0
\f1 \'b9\'d9\'b7\'ce
\f0
\f1 \'c0\'cc\'b5\'bf
\f0 \
* Clipboard Transformer:
\f1 \'ba\'b9\'bb\'e7\'c7\'d1
\f0
\f1 \'c5\'d8\'bd\'ba\'c6\'ae\'b8\'a6
\f0
\f1 \'b1\'d4\'c4\'a2\'bf\'a1
\f0
\f1 \'b5\'fb\'b6\'f3
\f0
\f1 \'b0\'a1\'b0\'f8\'c7\'d5\'b4\'cf\'b4\'d9
\f0 . (
\f1 \'bf\'b9
\f0 : JSON
\f1 \'c6\'f7\'b8\'cb\'c6\'c3
\f0 ,
\f1 \'c0\'af\'b4\'d0\'bd\'ba
\f0
\f1 \'c5\'b8\'c0\'d3\'bd\'ba\'c5\'c6\'c7\'c1
\f0
\f1 \'ba\'af\'c8\'af
\f0
\f1 \'b5\'ee
\f0 )\
3. UI/UX
\f1 \'b5\'f0\'c0\'da\'c0\'ce
\f0
\f1 \'b0\'a1\'c0\'cc\'b5\'e5
\f0 (WPF
\f1 \'b1\'e2\'b9\'dd
\f0 )\
\f1 \'bb\'e7\'bf\'eb\'c0\'da\'c0\'c7
\f0
\f1 \'bd\'c3\'b0\'a2\'c0\'fb
\f0
\f1 \'b9\'e6\'c7\'d8\'b8\'a6
\f0
\f1 \'c3\'d6\'bc\'d2\'c8\'ad\'c7\'cf\'b8\'e9\'bc\'ad
\f0
\f1 \'bc\'bc\'b7\'c3\'b5\'c8
\f0
\f1 \'b4\'c0\'b3\'a6\'c0\'bb
\f0
\f1 \'c1\'d6\'b4\'c2
\f0
\f1 \'b5\'f0\'c0\'da\'c0\'ce
\f0
\f1 \'b9\'e6\'c7\'e2\'c0\'d4\'b4\'cf\'b4\'d9
\f0 .\
|
\f1 \'bf\'e4\'bc\'d2
\f0 |
\f1 \'b5\'f0\'c0\'da\'c0\'ce
\f0
\f1 \'b1\'c7\'c0\'e5
\f0
\f1 \'bb\'e7\'c7\'d7
\f0 |\
|---|---|\
|
\f1 \'c3\'a2
\f0
\f1 \'c7\'fc\'c5\'c2
\f0 |
\f1 \'c8\'ad\'b8\'e9
\f0
\f1 \'c1\'df\'be\'d3\'bf\'a1
\f0
\f1 \'b6\'df\'b4\'c2
\f0
\f1 \'c5\'f5\'b8\'ed\'b5\'b5
\f0 (Opacity)
\f1 \'b0\'a1
\f0
\f1 \'c0\'fb\'bf\'eb\'b5\'c8
\f0
\f1 \'b9\'d9
\f0 (Bar)
\f1 \'c7\'fc\'c5\'c2
\f0 |\
|
\f1 \'c5\'d7\'b8\'b6
\f0 |
\f1 \'bd\'c3\'bd\'ba\'c5\'db
\f0
\f1 \'bc\'b3\'c1\'a4\'bf\'a1
\f0
\f1 \'b5\'fb\'b8\'a5
\f0
\f1 \'b4\'d9\'c5\'a9
\f0 /
\f1 \'b6\'f3\'c0\'cc\'c6\'ae
\f0
\f1 \'b8\'f0\'b5\'e5
\f0
\f1 \'c0\'da\'b5\'bf
\f0
\f1 \'c0\'fc\'c8\'af
\f0 |\
|
\f1 \'b0\'e1\'b0\'fa
\f0
\f1 \'b8\'ae\'bd\'ba\'c6\'ae
\f0 |
\f1 \'b8\'ed\'b7\'c9\'be\'ee
\f0
\f1 \'c0\'d4\'b7\'c2
\f0
\f1 \'bd\'c3
\f0
\f1 \'be\'c6\'b7\'a1\'b7\'ce
\f0
\f1 \'c3\'d6\'b4\'eb
\f0 5~7
\f1 \'b0\'b3\'c0\'c7
\f0
\f1 \'c3\'df\'c3\'b5
\f0
\f1 \'b8\'ae\'bd\'ba\'c6\'ae\'b0\'a1
\f0
\f1 \'b8\'ae\'bd\'ba\'c6\'ae\'ba\'e4
\f0
\f1 \'c7\'fc\'c5\'c2\'b7\'ce
\f0
\f1 \'c7\'a5\'bd\'c3
\f0 |\
|
\f1 \'be\'d6\'b4\'cf\'b8\'de\'c0\'cc\'bc\'c7
\f0 |
\f1 \'c8\'a3\'c3\'e2
\f0
\f1 \'bd\'c3
\f0
\f1 \'c6\'e4\'c0\'cc\'b5\'e5\'c0\'ce
\f0 (Fade-in),
\f1 \'bd\'c7\'c7\'e0
\f0
\f1 \'bd\'c3
\f0
\f1 \'bb\'e7\'b6\'f3\'c1\'f6\'b4\'c2
\f0
\f1 \'c8\'bf\'b0\'fa\'b7\'ce
\f0
\f1 \'bc\'d3\'b5\'b5\'b0\'a8
\f0
\f1 \'ba\'ce\'bf\'a9
\f0 |\
4.
\f1 \'b1\'b8\'c7\'f6\'c0\'bb
\f0
\f1 \'c0\'a7\'c7\'d1
\f0
\f1 \'c7\'d9\'bd\'c9
\f0
\f1 \'b1\'e2\'bc\'fa
\f0
\f1 \'c6\'f7\'c0\'ce\'c6\'ae
\f0 (C# .NET)\
\f1 \'c0\'cc
\f0
\f1 \'b5\'b5\'b1\'b8\'c0\'c7
\f0
\f1 \'bd\'c9\'c0\'e5\'c0\'ba
\f0 Windows API
\f1 \'c1\'a6\'be\'ee\'bf\'a1
\f0
\f1 \'c0\'d6\'bd\'c0\'b4\'cf\'b4\'d9
\f0 .\
*
\f1 \'c0\'a9\'b5\'b5\'bf\'ec
\f0
\f1 \'c1\'a6\'be\'ee
\f0 : User32.dll
\f1 \'c0\'c7
\f0 GetWindowPlacement, SetWindowPos, ShowWindow
\f1 \'c7\'d4\'bc\'f6\'b8\'a6
\f0
\f1 \'c5\'eb\'c7\'d8
\f0
\f1 \'b4\'d9\'b8\'a5
\f0
\f1 \'be\'db\'c0\'c7
\f0
\f1 \'c0\'a7\'c4\'a1\'bf\'cd
\f0
\f1 \'c5\'a9\'b1\'e2\'b8\'a6
\f0
\f1 \'b0\'ad\'c1\'a6
\f0
\f1 \'c1\'b6\'c1\'a4\'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
*
\f1 \'b1\'db\'b7\'ce\'b9\'fa
\f0
\f1 \'b4\'dc\'c3\'e0\'c5\'b0
\f0 : RegisterHotKey
\f1 \'b8\'a6
\f0
\f1 \'bb\'e7\'bf\'eb\'c7\'cf\'bf\'a9
\f0
\f1 \'bd\'c3\'bd\'ba\'c5\'db
\f0
\f1 \'c0\'fc\'c3\'bc\'bf\'a1\'bc\'ad
\f0
\f1 \'c0\'db\'b5\'bf\'c7\'cf\'b4\'c2
\f0
\f1 \'c7\'d6\'c5\'b0\'b8\'a6
\f0
\f1 \'b1\'b8\'c7\'f6\'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
*
\f1 \'b5\'a5\'c0\'cc\'c5\'cd
\f0
\f1 \'c0\'fa\'c0\'e5
\f0 :
\f1 \'ba\'b9\'c0\'e2\'c7\'d1
\f0 DB
\f1 \'b4\'eb\'bd\'c5
\f0 JSON
\f1 \'c6\'c4\'c0\'cf
\f0 (settings.json)
\f1 \'c0\'bb
\f0
\f1 \'bb\'e7\'bf\'eb\'c7\'cf\'bf\'a9
\f0
\f1 \'bb\'e7\'bf\'eb\'c0\'da\'b0\'a1
\f0
\f1 \'c1\'f7\'c1\'a2
\f0
\f1 \'c5\'d8\'bd\'ba\'c6\'ae
\f0
\f1 \'bf\'a1\'b5\'f0\'c5\'cd\'b7\'ce
\f0
\f1 \'ba\'b0\'c4\'aa\'c0\'bb
\f0
\f1 \'bc\'f6\'c1\'a4\'c7\'d2
\f0
\f1 \'bc\'f6
\f0
\f1 \'c0\'d6\'b0\'d4
\f0
\f1 \'c7\'d5\'b4\'cf\'b4\'d9
\f0 .\
}

575
AXCommander_SRS_v1.0.html Normal file
View File

@@ -0,0 +1,575 @@
<!DOCTYPE html>
<html lang="ko"><head><meta charset="utf-8">
<title>AXCommander_SRS_v1.0</title>
<style>
body { font-family: "맑은 고딕", "Malgun Gothic", sans-serif; font-size: 11pt; color: #1a1a1a; line-height: 1.7; max-width: 900px; margin: 0 auto; padding: 40px 20px; }
h1 { font-size: 22pt; color: #1F3864; border-bottom: 3px solid #4472C4; padding-bottom: 8px; margin-top: 32px; }
h2 { font-size: 16pt; color: #2E75B6; border-bottom: 2px solid #B8CCE4; padding-bottom: 4px; margin-top: 28px; }
h3 { font-size: 13pt; color: #44546A; margin-top: 22px; }
h4 { font-size: 12pt; color: #44546A; margin-top: 18px; }
p { margin: 6px 0; }
table { border-collapse: collapse; width: 100%; margin: 12px 0; }
th { background: #2E75B6; color: white; padding: 8px 10px; text-align: left; font-size: 10pt; }
td { padding: 6px 10px; border: 1px solid #D6E4F0; font-size: 10pt; }
tr:nth-child(even) td { background: #EBF3FB; }
ul, ol { margin: 6px 0 6px 24px; }
li { margin: 3px 0; }
code { font-family: Consolas, "D2Coding", monospace; background: #F0F0F0; padding: 1px 4px; border-radius: 3px; font-size: 10pt; }
.footer { text-align: right; color: #999; font-size: 9pt; margin-top: 40px; border-top: 1px solid #ddd; padding-top: 8px; }
</style></head><body>
<p>&lt;b&gt;OLEDi Commander&lt;/b&gt;</p>
<p>Software Requirements Specification</p>
<p>Windows 전용 시맨틱 런처 &amp; 워크스페이스 매니저</p>
<table>
<tr><th><b>항목</b></th><th><b>내용</b></th></tr>
<tr><td>문서 버전</td><td>v1.0</td></tr>
<tr><td>작성일</td><td>2026-03-21</td></tr>
<tr><td>최종 검토일</td><td>2026-03-21</td></tr>
<tr><td>상태</td><td>초안 (Draft)</td></tr>
<tr><td>대상 플랫폼</td><td>Windows 10 / 11 (x64)</td></tr>
<tr><td>구현 언어</td><td>C# / .NET 8 / WPF</td></tr>
</table>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;목차&lt;/b&gt;</h1>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;1. 개요 (Overview)&lt;/b&gt;</h1>
<h2>&lt;b&gt;1.1 프로젝트 목적&lt;/b&gt;</h2>
<p>OLEDi Commander는 macOS 생산성 도구 Alfred에서 영감을 받아, Windows 환경에서 동등하거나 그 이상의 생산성을 제공하기 위해 설계된 키보드 우선(Keyboard-first) 런처 겸 워크스페이스 매니저입니다.</p>
<p>전통적인 마우스 중심 UI 조작을 Alt+Space 단축키 하나로 대체하여, 개발자&#183;파워유저가 컨텍스트 전환 비용 없이 빠르게 작업을 수행할 수 있도록 합니다.</p>
<h2>&lt;b&gt;1.2 범위 (Scope)&lt;/b&gt;</h2>
<p>본 문서는 OLEDi Commander 1.0 릴리스를 대상으로 하며, 아래 두 핵심 모듈을 포함합니다.</p>
<ul>
<li>The Shifter: 워크스페이스 창 배치 스냅샷 및 복원</li>
<li>The Alfred: 시맨틱 커맨드 런처 (별칭, 클립보드 변환, Fuzzy 검색)</li>
</ul>
<p>아래 항목은 v1.0 범위에서 제외됩니다.</p>
<ul>
<li>macOS / Linux 지원</li>
<li>클라우드 동기화</li>
<li>플러그인 마켓플레이스 UI (v2.0 예정)</li>
</ul>
<h2>&lt;b&gt;1.3 용어 정의 (Glossary)&lt;/b&gt;</h2>
<table>
<tr><th><b>용어</b></th><th><b>정의</b></th></tr>
<tr><td>HWND</td><td>Windows API에서 창(Window)을 식별하는 핸들 값</td></tr>
<tr><td>Rect</td><td>창의 좌상단·우하단 좌표로 이루어진 사각형 구조체</td></tr>
<tr><td>Profile</td><td>특정 시점의 창 배치 및 크기 정보를 저장한 JSON 객체</td></tr>
<tr><td>Alias</td><td>긴 경로·명령을 짧게 치환하는 사용자 정의 키워드</td></tr>
<tr><td>Fuzzy Search</td><td>오타·부분 입력도 유사 항목을 찾아주는 비정확 검색</td></tr>
<tr><td>Global Hook</td><td>어떤 앱이 포커스를 갖고 있어도 키 입력을 감지하는 OS 훅</td></tr>
<tr><td>Plugin</td><td>CommandResolver에 새로운 ActionHandler를 추가하는 확장 단위</td></tr>
<tr><td>Skill</td><td>Plugin의 다른 표현. 사용자 정의 실행 규칙 묶음</td></tr>
<tr><td>settings.json</td><td>사용자 설정 및 Alias를 저장하는 로컬 JSON 파일</td></tr>
</table>
<h2>&lt;b&gt;1.4 참조 문서&lt;/b&gt;</h2>
<ul>
<li>Microsoft Docs User32.dll API Reference</li>
<li>WPF Documentation (.NET 8)</li>
<li>Windows Accessibility API (MSAA / UI Automation)</li>
<li>Alfred App https://www.alfredapp.com (인터페이스 참조)</li>
</ul>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;2. 시스템 아키텍처&lt;/b&gt;</h1>
<p>OLEDi Commander는 단일 프로세스(Single-process) 구조이며, 아래 5개 핵심 모듈로 구성됩니다. 모든 모듈은 의존성 주입(DI) 방식으로 연결되어 테스트 및 확장이 용이합니다.</p>
<table>
<tr><th><b>모듈</b></th><th><b>역할</b></th><th><b>핵심 기술</b></th></tr>
<tr><td>Input Listener</td><td>글로벌 키 훅 어떤 앱에서도 Alt+Space 감지</td><td>WH_KEYBOARD_LL, RegisterHotKey</td></tr>
<tr><td>Context Manager</td><td>열린 창의 HWND, Rect, 프로세스 경로 수집 및 복원</td><td>EnumWindows, GetWindowPlacement, SetWindowPos</td></tr>
<tr><td>Command Resolver</td><td>입력 텍스트 파싱 → ActionHandler 라우팅</td><td>Prefix 테이블, ActionHandler 인터페이스</td></tr>
<tr><td>Fuzzy Engine</td><td>파일명·키워드 부분 입력으로 빠른 유사 항목 탐색</td><td>Fuse.js 알고리즘 포팅 또는 FuzzySharp</td></tr>
<tr><td>Plugin Host</td><td>외부 .dll 또는 JSON 기반 스킬을 로드·실행</td><td>Reflection, IActionHandler 인터페이스</td></tr>
</table>
<h2>&lt;b&gt;2.1 모듈 간 데이터 흐름&lt;/b&gt;</h2>
<p>[사용자 키 입력] → Input Listener → Command Resolver → ActionHandler (Shifter / Alfred / Plugin)</p>
<p>[창 배치 저장] → Context Manager → Profile JSON → settings.json</p>
<p>[창 배치 복원] → settings.json → Context Manager → User32 API</p>
<h2>&lt;b&gt;2.2 Prefix 라우팅 테이블&lt;/b&gt;</h2>
<p>Command Resolver는 입력 텍스트의 첫 번째 토큰(prefix)을 기준으로 ActionHandler를 선택합니다. 빌트인 prefix는 다음과 같습니다.</p>
<table>
<tr><th><b>Prefix</b></th><th><b>타입</b></th><th><b>예시</b></th><th><b>동작</b></th></tr>
<tr><td>!</td><td>워크스페이스</td><td>!dev</td><td>저장된 "dev" 프로필 즉시 복원</td></tr>
<tr><td>@</td><td>URL / 웹</td><td>@blog</td><td>settings.json의 해당 URL 브라우저로 오픈</td></tr>
<tr><td>#</td><td>동적 API</td><td>#jira</td><td>설정된 API 어댑터 호출 후 결과 표시</td></tr>
<tr><td>></td><td>터미널 명령</td><td>>git status</td><td>Windows Terminal / PowerShell에서 실행</td></tr>
<tr><td>~</td><td>파일 경로</td><td>~projects</td><td>등록된 폴더 경로를 탐색기로 오픈</td></tr>
<tr><td>(없음)</td><td>Fuzzy 검색</td><td>vsc</td><td>인덱스에서 유사 파일·앱·Alias 검색</td></tr>
</table>
<table>
<tr><th><b>확장 가능성</b><br/>위 prefix 목록은 settings.json의 "prefixMap" 배열을 통해 사용자가 직접 추가·변경할 수 있습니다.<br/>플러그인 개발자는 IActionHandler 인터페이스를 구현하는 .dll을 제공하여 새로운 prefix 동작을 등록합니다.<br/>자세한 내용은 섹션 7 개발자 확장 가이드를 참조하십시오.</th></tr>
</table>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;3. 기능 요구사항&lt;/b&gt;</h1>
<h2>&lt;b&gt;3.1 워크스페이스 스냅샷 &amp; 시프트 (The Shifter)&lt;/b&gt;</h2>
<p>&quot;어떤 위치에 어떤 크기로&quot; 떠 있는지까지 관리하는 워크스페이스 레이아웃 엔진입니다. 단순 앱 실행을 넘어 창의 상태(State)를 완전히 복원합니다.</p>
<h3>&lt;b&gt;3.1.1 Snapshot Capture&lt;/b&gt;</h3>
<ul>
<li>현재 화면에 있는 모든 가시(Visible) 업무용 창의 배치를 &quot;프로필&quot;로 저장</li>
<li>저장 정보: HWND, 프로세스 실행 경로(EXE), 창 제목, Rect (좌상단 x/y, 폭, 높이), 최소화/최대화/일반 상태(ShowCmd)</li>
<li>저장 위치: settings.json의 &quot;profiles&quot; 배열</li>
<li>저장 명령어: !save &lt;프로필명&gt; 또는 UI의 저장 버튼</li>
<li>시스템 트레이 UI(Task Bar) 및 바탕화면은 캡처 제외</li>
</ul>
<h3>&lt;b&gt;3.1.2 Instant Restore&lt;/b&gt;</h3>
<ul>
<li>!&lt;프로필명&gt; 입력 시 해당 프로필의 창 배치를 강제 복원</li>
<li>복원 순서: 1) 저장된 EXE 경로로 프로세스 존재 확인 → 2) HWND 유효성 검사 → 3) SetWindowPos 호출</li>
<li>앱이 실행 중이지 않은 경우: 자동으로 EXE를 실행한 후 창 생성을 최대 3초간 대기</li>
<li>3초 내 창이 생성되지 않으면 해당 앱을 건너뛰고 나머지 복원 진행, 결과 알림 표시</li>
<li>관리자 권한 창(elevated process)은 복원 시 UAC 프롬프트 없이 위치만 조정 (권한 상승 없이 처리 가능한 범위 내에서 수행)</li>
</ul>
<h3>&lt;b&gt;3.1.3 Profile 관리&lt;/b&gt;</h3>
<ul>
<li>목록 보기: 런처 UI에서 !&lt;엔터&gt; 입력 시 저장된 프로필 전체 목록 표시</li>
<li>이름 변경: !rename &lt;현재명&gt; &lt;새이름&gt;</li>
<li>삭제: !delete &lt;프로필명&gt; (확인 다이얼로그 필요)</li>
<li>내보내기/가져오기: settings.json 파일 직접 공유 가능 (JSON 스키마 준수 시)</li>
</ul>
<h3>&lt;b&gt;3.1.4 Multi-Monitor 지원&lt;/b&gt;</h3>
<ul>
<li>각 모니터의 DPI 및 좌표계 독립 저장</li>
<li>모니터 구성 변경 감지: WM_DISPLAYCHANGE 메시지 수신 시 활성 프로필과 현재 모니터 구성 비교</li>
<li>모니터 불일치 시 동작 옵션 (settings.json으로 설정 가능):</li>
<li>&quot;fit&quot;: 사용 가능한 모니터에 좌표 스케일링하여 배치</li>
<li>&quot;skip&quot;: 해당 모니터의 창만 건너뜀</li>
<li>&quot;warn&quot;: 복원 전 경고 팝업 표시 (기본값)</li>
</ul>
<h2>&lt;b&gt;3.2 시맨틱 커맨드 런처 (The Alfred)&lt;/b&gt;</h2>
<p>키보드만으로 마우스 클릭 수십 번의 가치를 만들어냅니다. Alt+Space → 명령어 입력의 단일 패턴으로 모든 작업을 처리합니다.</p>
<h3>&lt;b&gt;3.2.1 Smart Aliases&lt;/b&gt;</h3>
<p>긴 경로나 복잡한 명령을 짧은 별칭으로 등록합니다. settings.json의 &quot;aliases&quot; 배열로 관리됩니다.</p>
<table>
<tr><th><b>Alias 타입</b></th><th><b>예시</b></th><th><b>동작 설명</b></th></tr>
<tr><td>url</td><td>@blog → https://swarchitect.net/admin</td><td>브라우저로 URL 오픈</td></tr>
<tr><td>folder</td><td>~proj → C:\\Dev\\Projects</td><td>탐색기로 폴더 오픈</td></tr>
<tr><td>app</td><td>@term → C:\\Windows\\wt.exe</td><td>지정 실행파일 실행</td></tr>
<tr><td>batch</td><td>>build → cmd /c build.bat</td><td>커맨드 실행 (출력 창 선택)</td></tr>
<tr><td>api</td><td>#jira → JiraAdapter (토큰 설정 필요)</td><td>동적 데이터 조회 후 결과 표시</td></tr>
<tr><td>clipboard</td><td>$upper → UPPER_CASE 변환</td><td>현재 클립보드 텍스트 변환</td></tr>
</table>
<h3>&lt;b&gt;3.2.2 Fuzzy Engine&lt;/b&gt;</h3>
<ul>
<li>검색 범위: 사용자 정의 인덱스 폴더(기본: 바탕화면, 시작 메뉴, 최근 파일) + 등록된 모든 Alias</li>
<li>인덱싱: 앱 시작 시 1회 전체 인덱싱, 이후 FileSystemWatcher로 변경 감지 시 증분 업데이트</li>
<li>응답 목표: 타이핑 후 100ms 이내에 결과 표시 (로컬 인덱스 기준)</li>
<li>결과 랭킹 기준: 1) 정확히 일치하는 Alias 최우선, 2) 문자열 유사도, 3) 최근 실행 빈도 가중치</li>
<li>최대 결과 수: UI에 5~7개 표시 (설정으로 최대 20개까지 확장 가능)</li>
<li>대소문자 구분 없음, 한글 초성 검색 지원 (예: &quot;ㅂㅅ&quot;&quot;Visual Studio&quot;)</li>
</ul>
<h3>&lt;b&gt;3.2.3 Clipboard Transformer&lt;/b&gt;</h3>
<p>복사한 텍스트를 규칙에 따라 가공합니다. $ prefix로 호출하며, 변환 결과를 클립보드에 덮어쓴 후 활성 창에 붙여넣기(Ctrl+V) 시뮬레이션을 수행합니다.</p>
<table>
<tr><th><b>빌트인 변환</b></th><th><b>명령</b></th><th><b>예시</b></th></tr>
<tr><td>JSON 포맷팅</td><td>$json</td><td>{"a":1} → 들여쓰기 적용 JSON</td></tr>
<tr><td>유닉스 타임스탬프 → 날짜</td><td>$ts</td><td>1700000000 → 2023-11-14 22:13:20</td></tr>
<tr><td>날짜 → 유닉스 타임스탬프</td><td>$epoch</td><td>2023-11-14 → 1700000000</td></tr>
<tr><td>대문자 변환</td><td>$upper</td><td>hello → HELLO</td></tr>
<tr><td>소문자 변환</td><td>$lower</td><td>HELLO → hello</td></tr>
<tr><td>URL 인코딩</td><td>$urle</td><td>한글 → %ED%95%9C%EA%B8%80</td></tr>
<tr><td>URL 디코딩</td><td>$urld</td><td>%ED%95%9C%EA%B8%80 → 한글</td></tr>
<tr><td>Base64 인코딩</td><td>$b64e</td><td>text → dGV4dA==</td></tr>
<tr><td>Base64 디코딩</td><td>$b64d</td><td>dGV4dA== → text</td></tr>
<tr><td>마크다운 → 텍스트</td><td>$md</td><td>**bold** → bold</td></tr>
</table>
<p>사용자 정의 변환 규칙은 settings.json의 &quot;clipboardTransformers&quot; 배열에 추가하며, 정규식 기반 변환과 외부 스크립트 호출을 지원합니다. 자세한 내용은 섹션 7.4를 참조하십시오.</p>
<h2>&lt;b&gt;3.3 동적 API Alias (#)&lt;/b&gt;</h2>
<p># prefix를 사용하는 Alias는 외부 API를 호출하여 동적 결과를 표시합니다. 각 API 연결은 &quot;apiAdapters&quot; 설정으로 정의합니다.</p>
<table>
<tr><th><b>항목</b></th><th><b>요구사항</b></th></tr>
<tr><td>인증</td><td>Personal Access Token(PAT) 또는 OAuth 2.0. 토큰은 Windows Credential Manager에 저장</td></tr>
<tr><td>네트워크 오류</td><td>3초 내 응답 없으면 타임아웃, 오프라인 캐시(최대 1시간) 표시</td></tr>
<tr><td>결과 표시</td><td>최대 10개 항목을 런처 결과 리스트에 표시, Enter로 기본 동작(URL 오픈) 실행</td></tr>
<tr><td>빌트인 어댑터</td><td>Jira Cloud, GitHub Issues (v1.0). 추가 어댑터는 플러그인으로 제공</td></tr>
</table>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;4. UI/UX 디자인 요구사항&lt;/b&gt;</h1>
<h2>&lt;b&gt;4.1 런처 윈도우&lt;/b&gt;</h2>
<table>
<tr><th><b>요소</b></th><th><b>명세</b></th></tr>
<tr><td>형태</td><td>화면 중앙 상단 1/3 지점에 위치하는 반투명 바(Bar) 형태 오버레이</td></tr>
<tr><td>기본 크기</td><td>너비: 680px, 높이: 54px (입력 상태). 결과 표시 시 높이 자동 확장</td></tr>
<tr><td>투명도</td><td>Opacity 0.96 (기본). settings.json에서 0.7~1.0 조정 가능</td></tr>
<tr><td>테마</td><td>시스템 다크/라이트 모드 자동 감지(WMI 또는 레지스트리 감시). 수동 고정 설정 가능</td></tr>
<tr><td>폰트</td><td>Segoe UI (영문), Malgun Gothic (한글), 16px</td></tr>
<tr><td>애니메이션</td><td>호출: 상단 20px에서 Fade-in + SlideDown (120ms). 닫힘: Fade-out (80ms)</td></tr>
<tr><td>항상 최상위</td><td>Topmost = true. 다른 창에 가려지지 않음</td></tr>
<tr><td>포커스 처리</td><td>런처 외부 클릭 시 자동 닫힘 (LostFocus 이벤트)</td></tr>
</table>
<h2>&lt;b&gt;4.2 결과 리스트&lt;/b&gt;</h2>
<ul>
<li>최대 5~7개 항목 표시 (기본 5개, settings.json에서 조정)</li>
<li>각 항목: 아이콘(32x32) + 제목 + 부제목(경로 또는 설명)</li>
<li>키보드 탐색: ↑↓ 화살표, Enter로 실행, Esc로 닫기, Tab으로 자동완성</li>
<li>마우스 클릭으로도 실행 가능</li>
<li>결과 없음 시: &quot;일치하는 항목이 없습니다&quot; 메시지 표시</li>
</ul>
<h2>&lt;b&gt;4.3 접근성 (Accessibility)&lt;/b&gt;</h2>
<ul>
<li>UI Automation(UIA) 속성 제공으로 스크린 리더 지원</li>
<li>고대비(High Contrast) 모드 지원</li>
<li>글꼴 크기 배율: 시스템 DPI 설정 반영 (100% / 125% / 150% / 200%)</li>
<li>Alt+Space 단축키는 settings.json에서 변경 가능</li>
</ul>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;5. 비기능 요구사항 (NFR)&lt;/b&gt;</h1>
<h2>&lt;b&gt;5.1 성능&lt;/b&gt;</h2>
<table>
<tr><th><b>항목</b></th><th><b>목표 수치</b></th><th><b>측정 방법</b></th></tr>
<tr><td>Fuzzy 검색 응답</td><td>< 100ms (p95)</td><td>로컬 인덱스 10,000건 기준 자동화 벤치마크</td></tr>
<tr><td>Instant Restore (5창 기준)</td><td>< 1.5초</td><td>스톱워치 측정, 5회 평균</td></tr>
<tr><td>앱 시작 시간</td><td>< 800ms (백그라운드 Ready 상태)</td><td>프로세스 시작 → 트레이 아이콘 표시까지</td></tr>
<tr><td>메모리 사용량</td><td>유휴 시 < 80MB RSS</td><td>Process Explorer 모니터링</td></tr>
<tr><td>CPU 점유율</td><td>유휴 시 < 0.5%</td><td>10분 유휴 후 평균</td></tr>
<tr><td>인덱싱 시간</td><td>10,000파일 < 3초</td><td>초기 기동 시 측정</td></tr>
</table>
<h2>&lt;b&gt;5.2 신뢰성 &amp; 안정성&lt;/b&gt;</h2>
<ul>
<li>Global Hook 스레드 예외 시 자동 재등록 (최대 3회 재시도, 이후 트레이 경고 알림)</li>
<li>앱 크래시 시 Windows Error Reporting 연동 및 로컬 크래시 덤프 저장 (%APPDATA%\OLEDiCommander\crashes\)</li>
<li>settings.json 파싱 실패 시 기본값(default) 설정으로 폴백, 원본 파일은 .bak 확장자로 백업</li>
<li>MTBF(평균 무고장 시간) 목표: 7일 연속 사용 시 크래시 없음</li>
</ul>
<h2>&lt;b&gt;5.3 보안&lt;/b&gt;</h2>
<ul>
<li>Global Keyboard Hook(WH_KEYBOARD_LL): 키 입력을 로깅하지 않음. Alt+Space 패턴 감지 후 즉시 버퍼 파기</li>
<li>API 토큰: Windows Credential Manager(DPAPI) 저장, settings.json에 평문 토큰 기록 금지</li>
<li>플러그인 .dll: 디지털 서명 검증 권장 (v1.0에서는 경고 표시, v2.0에서 강제 서명 요구 예정)</li>
<li>UAC: 앱 자체는 일반 사용자 권한으로 실행. 관리자 권한이 필요한 동작 시 명시적 UAC 프롬프트</li>
<li>네트워크 통신: HTTPS only (TLS 1.2+). 자체 서명 인증서 거부</li>
</ul>
<table>
<tr><th><b>보안 주의사항 Windows Defender / EDR 탐지</b><br/>WH_KEYBOARD_LL 훅은 일부 보안 소프트웨어에서 키로거로 오탐될 수 있습니다.<br/>대응 방안: 앱 배포 시 코드 서명 인증서 적용, Microsoft Store 등록 검토.<br/>기업 환경 배포 시에는 GPO 화이트리스트 등록 가이드를 별도 제공합니다.</th></tr>
</table>
<h2>&lt;b&gt;5.4 유지보수성&lt;/b&gt;</h2>
<ul>
<li>모듈 간 의존성: DI 컨테이너(Microsoft.Extensions.DependencyInjection) 사용</li>
<li>로깅: Serilog 사용, 레벨(DEBUG/INFO/WARN/ERROR) 설정 가능. 로그 파일: %APPDATA%\OLEDiCommander\logs\</li>
<li>단위 테스트 커버리지: 핵심 모듈(CommandResolver, FuzzyEngine) 80% 이상 목표</li>
<li>업데이트: Squirrel.Windows 또는 MSIX 패키지 업데이트 채널 사용</li>
</ul>
<h2>&lt;b&gt;5.5 호환성&lt;/b&gt;</h2>
<ul>
<li>최소 지원 OS: Windows 10 (버전 1903, 빌드 18362) 이상</li>
<li>권장 OS: Windows 11</li>
<li>.NET 런타임: .NET 8.0 (Self-contained 배포로 별도 설치 불필요)</li>
<li>DPI 지원: System DPI 및 Per-Monitor DPI v2 (WPF DPI 자동 스케일링)</li>
<li>멀티 모니터: 동일 DPI 및 혼합 DPI 환경 모두 지원</li>
</ul>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;6. 예외 처리 및 엣지 케이스&lt;/b&gt;</h1>
<table>
<tr><th><b>시나리오</b></th><th><b>원인</b></th><th><b>처리 방침</b></th></tr>
<tr><td>Restore 시 앱 미실행</td><td>저장된 EXE가 닫혀 있음</td><td>자동 EXE 실행 → 3초 대기 → 실패 시 건너뜀, 결과 알림</td></tr>
<tr><td>모니터 구성 변경</td><td>모니터 추가/제거/해상도 변경</td><td>settings.json의 "monitorMismatch" 정책에 따라 fit/skip/warn</td></tr>
<tr><td>단축키 충돌</td><td>타 앱이 Alt+Space 선점</td><td>등록 실패 감지 → 트레이에 경고 → 대체 단축키 제안</td></tr>
<tr><td>관리자 권한 창 제어</td><td>타 프로세스가 elevated</td><td>SetWindowPos 예외 캐치 → 해당 창 건너뜀, 로그 기록</td></tr>
<tr><td>settings.json 손상</td><td>JSON 파싱 오류</td><td>원본 .bak 저장 → 기본값 로드 → 복구 안내 메시지</td></tr>
<tr><td>Alias 중복 등록</td><td>동일 키워드 중복</td><td>나중에 추가된 항목이 우선. 경고 로그 기록</td></tr>
<tr><td>플러그인 로드 실패</td><td>.dll 서명 불일치 또는 예외</td><td>해당 플러그인 건너뜀, 오류 로그. 앱은 계속 실행</td></tr>
<tr><td>API 타임아웃</td><td>네트워크 불안정</td><td>3초 타임아웃 → 오프라인 캐시 표시 (1시간 유효) → 캐시 없으면 오류 메시지</td></tr>
<tr><td>앱 인덱스 폴더 접근 불가</td><td>권한 없는 폴더</td><td>해당 폴더 스킵, 인덱싱 완료 후 경고 로그</td></tr>
<tr><td>Fuzzy 검색 결과 없음</td><td>입력에 일치 항목 없음</td><td>"일치하는 항목이 없습니다" 표시, 입력 지속 허용</td></tr>
</table>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;7. 데이터 스키마 (settings.json)&lt;/b&gt;</h1>
<p>모든 사용자 설정과 Profile, Alias는 단일 JSON 파일로 관리됩니다. 파일 위치: %APPDATA%\OLEDiCommander\settings.json</p>
<p>{</p>
<p> &quot;version&quot;: &quot;1.0&quot;,</p>
<p> &quot;hotkey&quot;: &quot;Alt+Space&quot;,</p>
<p> &quot;launcher&quot;: {</p>
<p> &quot;opacity&quot;: 0.96,</p>
<p> &quot;maxResults&quot;: 7,</p>
<p> &quot;theme&quot;: &quot;system&quot;,</p>
<p> &quot;position&quot;: &quot;center-top&quot;</p>
<p> },</p>
<p> &quot;indexPaths&quot;: [</p>
<p> &quot;%USERPROFILE%\\Desktop&quot;,</p>
<p> &quot;%APPDATA%\\Microsoft\\Windows\\Start Menu&quot;</p>
<p> ],</p>
<p> &quot;monitorMismatch&quot;: &quot;warn&quot;,</p>
<p> &quot;profiles&quot;: [</p>
<p> {</p>
<p> &quot;name&quot;: &quot;dev&quot;,</p>
<p> &quot;windows&quot;: [</p>
<p> {</p>
<p> &quot;exe&quot;: &quot;C:\\Program Files\\Microsoft VS Code\\Code.exe&quot;,</p>
<p> &quot;title&quot;: &quot;Visual Studio Code&quot;,</p>
<p> &quot;rect&quot;: { &quot;x&quot;: 0, &quot;y&quot;: 0, &quot;width&quot;: 1280, &quot;height&quot;: 1080 },</p>
<p> &quot;showCmd&quot;: &quot;Normal&quot;,</p>
<p> &quot;monitor&quot;: 0</p>
<p> }</p>
<p> ]</p>
<p> }</p>
<p> ],</p>
<p> &quot;aliases&quot;: [</p>
<p> { &quot;key&quot;: &quot;@blog&quot;, &quot;type&quot;: &quot;url&quot;, &quot;target&quot;: &quot;https://swarchitect.net/admin&quot; },</p>
<p> { &quot;key&quot;: &quot;#jira&quot;, &quot;type&quot;: &quot;api&quot;, &quot;adapter&quot;: &quot;jira&quot;, &quot;query&quot;: &quot;assignee=currentUser() ORDER BY updated DESC&quot; },</p>
<p> { &quot;key&quot;: &quot;~proj&quot;, &quot;type&quot;: &quot;folder&quot;, &quot;target&quot;: &quot;C:\\Dev\\Projects&quot; },</p>
<p> { &quot;key&quot;: &quot;&gt;build&quot;, &quot;type&quot;: &quot;batch&quot;, &quot;target&quot;: &quot;cmd /c C:\\Dev\\build.bat&quot;, &quot;showWindow&quot;: false }</p>
<p> ],</p>
<p> &quot;clipboardTransformers&quot;: [</p>
<p> { &quot;key&quot;: &quot;$myRule&quot;, &quot;type&quot;: &quot;regex&quot;, &quot;pattern&quot;: &quot;(\\d{4})-(\\d{2})-(\\d{2})&quot;, &quot;replace&quot;: &quot;$3/$2/$1&quot; }</p>
<p> ],</p>
<p> &quot;apiAdapters&quot;: [</p>
<p> { &quot;id&quot;: &quot;jira&quot;, &quot;baseUrl&quot;: &quot;https://yourorg.atlassian.net&quot;, &quot;credentialKey&quot;: &quot;jira_pat&quot; }</p>
<p> ],</p>
<p> &quot;plugins&quot;: [</p>
<p> { &quot;path&quot;: &quot;C:\\OLEDiPlugins\\MyPlugin.dll&quot;, &quot;enabled&quot;: true }</p>
<p> ]</p>
<p>}</p>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;8. 개발자 확장 가이드 (Plugin &amp; Skill Development)&lt;/b&gt;</h1>
<p>이 섹션은 OLEDi Commander에 새로운 기능, 명령어, API 연결, 변환 규칙을 추가하려는 개발자를 위한 문서입니다. 확장 방법은 세 가지 경로로 제공됩니다.</p>
<table>
<tr><th><b>확장 방법</b></th><th><b>난이도</b></th><th><b>추천 대상</b></th></tr>
<tr><td>settings.json 수정</td><td>쉬움</td><td>URL Alias, 폴더 단축키, 배치 명령 추가</td></tr>
<tr><td>JSON 스킬 파일 (.skill.json)</td><td>보통</td><td>API 어댑터, 커스텀 변환 규칙</td></tr>
<tr><td>.dll 플러그인 (C# IActionHandler)</td><td>어려움</td><td>완전히 새로운 명령 타입, UI 커스터마이징</td></tr>
</table>
<h2>&lt;b&gt;8.1 settings.json으로 Alias 추가하기&lt;/b&gt;</h2>
<p>가장 간단한 확장 방법입니다. settings.json 파일을 텍스트 에디터로 열어 &quot;aliases&quot; 배열에 항목을 추가합니다.</p>
<p>// URL 열기 Alias 추가</p>
<p>{ &quot;key&quot;: &quot;@notion&quot;, &quot;type&quot;: &quot;url&quot;, &quot;target&quot;: &quot;https://notion.so/your-workspace&quot; }</p>
<p>// 폴더 열기 Alias 추가</p>
<p>{ &quot;key&quot;: &quot;~dl&quot;, &quot;type&quot;: &quot;folder&quot;, &quot;target&quot;: &quot;%USERPROFILE%\\Downloads&quot; }</p>
<p>// 배치 파일 실행 Alias 추가 (출력 창 표시)</p>
<p>{ &quot;key&quot;: &quot;&gt;deploy&quot;, &quot;type&quot;: &quot;batch&quot;, &quot;target&quot;: &quot;cmd /c C:\\deploy.bat&quot;, &quot;showWindow&quot;: true }</p>
<p>// 배치 파일 실행 Alias 추가 (백그라운드 실행)</p>
<p>{ &quot;key&quot;: &quot;&gt;silent&quot;, &quot;type&quot;: &quot;batch&quot;, &quot;target&quot;: &quot;powershell -File C:\\task.ps1&quot;, &quot;showWindow&quot;: false }</p>
<table>
<tr><th><b>팁: 환경변수 사용</b><br/>"target" 값에 %USERPROFILE%, %APPDATA%, %TEMP% 등 Windows 환경변수를 사용할 수 있습니다.<br/>앱 실행 시 자동으로 확장됩니다.</th></tr>
</table>
<h2>&lt;b&gt;8.2 Clipboard Transformer 규칙 추가하기&lt;/b&gt;</h2>
<p>settings.json의 &quot;clipboardTransformers&quot; 배열에 변환 규칙을 추가합니다. 정규식(regex) 또는 외부 스크립트(script) 타입을 지원합니다.</p>
<p>// 정규식 변환: 날짜 형식 YYYY-MM-DD → DD/MM/YYYY</p>
<p>{</p>
<p> &quot;key&quot;: &quot;$date&quot;,</p>
<p> &quot;type&quot;: &quot;regex&quot;,</p>
<p> &quot;pattern&quot;: &quot;(\\d{4})-(\\d{2})-(\\d{2})&quot;,</p>
<p> &quot;replace&quot;: &quot;$3/$2/$1&quot;,</p>
<p> &quot;description&quot;: &quot;ISO 날짜를 로컬 날짜 형식으로 변환&quot;</p>
<p>}</p>
<p>// PowerShell 스크립트 호출 변환</p>
<p>{</p>
<p> &quot;key&quot;: &quot;$ps&quot;,</p>
<p> &quot;type&quot;: &quot;script&quot;,</p>
<p> &quot;command&quot;: &quot;powershell -NoProfile -Command \&quot;$input | ConvertTo-Json\&quot;&quot;,</p>
<p> &quot;timeout&quot;: 5000,</p>
<p> &quot;description&quot;: &quot;클립보드 텍스트를 PowerShell로 처리&quot;</p>
<p>}</p>
<p>스크립트 타입의 경우, 앱은 클립보드 텍스트를 stdin으로 전달하고 stdout 결과를 클립보드에 저장합니다. timeout(ms) 초과 시 원본 텍스트를 유지합니다.</p>
<h2>&lt;b&gt;8.3 JSON 스킬 파일로 API Adapter 추가하기&lt;/b&gt;</h2>
<p>새로운 API 서비스(예: Notion, Linear, Slack)를 연결하려면 .skill.json 파일을 작성합니다. 파일은 %APPDATA%\OLEDiCommander\skills\ 폴더에 저장합니다.</p>
<p>// %APPDATA%\OLEDiCommander\skills\notion.skill.json</p>
<p>{</p>
<p> &quot;id&quot;: &quot;notion&quot;,</p>
<p> &quot;name&quot;: &quot;Notion 페이지 검색&quot;,</p>
<p> &quot;version&quot;: &quot;1.0&quot;,</p>
<p> &quot;prefix&quot;: &quot;#notion&quot;,</p>
<p> &quot;credential&quot;: {</p>
<p> &quot;type&quot;: &quot;bearer_token&quot;,</p>
<p> &quot;credentialKey&quot;: &quot;notion_api_token&quot;</p>
<p> },</p>
<p> &quot;request&quot;: {</p>
<p> &quot;method&quot;: &quot;POST&quot;,</p>
<p> &quot;url&quot;: &quot;https://api.notion.com/v1/search&quot;,</p>
<p> &quot;headers&quot;: { &quot;Notion-Version&quot;: &quot;2022-06-28&quot; },</p>
<p> &quot;body&quot;: { &quot;query&quot;: &quot;{{INPUT}}&quot;, &quot;page_size&quot;: 10 }</p>
<p> },</p>
<p> &quot;response&quot;: {</p>
<p> &quot;resultsPath&quot;: &quot;results&quot;,</p>
<p> &quot;titleField&quot;: &quot;properties.title.title[0].plain_text&quot;,</p>
<p> &quot;subtitleField&quot;: &quot;url&quot;,</p>
<p> &quot;actionUrl&quot;: &quot;url&quot;</p>
<p> },</p>
<p> &quot;cache&quot;: { &quot;ttl&quot;: 300 }</p>
<p>}</p>
<p>{{INPUT}}은 사용자가 # 이후에 입력한 텍스트로 치환됩니다. 토큰은 앱 내 설정 화면에서 입력하며 Windows Credential Manager에 암호화 저장됩니다.</p>
<table>
<tr><th><b>필드</b></th><th><b>필수</b></th><th><b>설명</b></th></tr>
<tr><td>id</td><td></td><td>스킬 고유 식별자 (영문 소문자, 하이픈 허용)</td></tr>
<tr><td>prefix</td><td></td><td>런처에서 이 스킬을 호출하는 명령어 (예: #notion)</td></tr>
<tr><td>credential.type</td><td></td><td>bearer_token / basic_auth / oauth2 중 선택</td></tr>
<tr><td>request.url</td><td></td><td>API 엔드포인트. {{INPUT}}, {{TOKEN}} 템플릿 변수 사용 가능</td></tr>
<tr><td>response.resultsPath</td><td></td><td>JSON 응답에서 결과 배열 경로 (dot notation)</td></tr>
<tr><td>response.actionUrl</td><td></td><td>Enter 시 열리는 URL 필드 경로</td></tr>
<tr><td>cache.ttl</td><td>아니오</td><td>결과 캐시 유효 시간(초). 기본값 0 (캐시 없음)</td></tr>
</table>
<h2>&lt;b&gt;8.4 C# .dll 플러그인으로 새 ActionHandler 개발하기&lt;/b&gt;</h2>
<p>완전히 새로운 명령 타입, 복잡한 UI, 또는 OS 레벨 기능이 필요한 경우 C# 플러그인을 개발합니다.</p>
<h3>&lt;b&gt;8.4.1 인터페이스 정의&lt;/b&gt;</h3>
<p>// OLEDiCommander.SDK NuGet 패키지 설치 후 사용</p>
<p>using OLEDiCommander.SDK;</p>
<p>/// &lt;summary&gt;</p>
<p>/// 모든 플러그인은 이 인터페이스를 구현해야 합니다.</p>
<p>/// &lt;/summary&gt;</p>
<p>public interface IActionHandler</p>
<p>{</p>
<p> // 이 핸들러가 처리할 prefix (예: &quot;@&quot;, &quot;#&quot;, &quot;!&quot;) 또는 null (Fuzzy 결과에만 등록)</p>
<p> string? Prefix { get; }</p>
<p> // 런처 결과 리스트에 표시할 항목을 반환합니다.</p>
<p> // query: prefix 이후의 입력 텍스트</p>
<p> Task&lt;IEnumerable&lt;LauncherItem&gt;&gt; GetItemsAsync(string query, CancellationToken ct);</p>
<p> // 사용자가 항목을 선택(Enter)했을 때 실행됩니다.</p>
<p> Task ExecuteAsync(LauncherItem item, CancellationToken ct);</p>
<p> // 플러그인 메타데이터</p>
<p> PluginMetadata Metadata { get; }</p>
<p>}</p>
<p>public record LauncherItem(</p>
<p> string Title,</p>
<p> string Subtitle,</p>
<p> string? IconPath, // null이면 기본 아이콘 사용</p>
<p> object? Data // ExecuteAsync에 전달되는 임의 데이터</p>
<p>);</p>
<p>public record PluginMetadata(</p>
<p> string Id,</p>
<p> string Name,</p>
<p> string Version,</p>
<p> string Author</p>
<p>);</p>
<h3>&lt;b&gt;8.4.2 예제: 계산기 플러그인&lt;/b&gt;</h3>
<p>using OLEDiCommander.SDK;</p>
<p>using System.Data;</p>
<p>[Export(typeof(IActionHandler))]</p>
<p>public class CalculatorHandler : IActionHandler</p>
<p>{</p>
<p> public string? Prefix =&gt; &quot;=&quot;; // &quot;=2+3&quot; 입력 시 이 핸들러 호출</p>
<p> public PluginMetadata Metadata =&gt; new(&quot;calculator&quot;, &quot;계산기&quot;, &quot;1.0&quot;, &quot;YourName&quot;);</p>
<p> public async Task&lt;IEnumerable&lt;LauncherItem&gt;&gt; GetItemsAsync(string query, CancellationToken ct)</p>
<p> {</p>
<p> try</p>
<p> {</p>
<p> var result = new DataTable().Compute(query, null);</p>
<p> return [new LauncherItem($&quot;= {result}&quot;, &quot;Enter로 복사&quot;, null, result.ToString())];</p>
<p> }</p>
<p> catch</p>
<p> {</p>
<p> return [new LauncherItem(&quot;수식 오류&quot;, &quot;올바른 수식을 입력하세요&quot;, null, null)];</p>
<p> }</p>
<p> }</p>
<p> public async Task ExecuteAsync(LauncherItem item, CancellationToken ct)</p>
<p> {</p>
<p> if (item.Data is string val)</p>
<p> Clipboard.SetText(val); // 결과를 클립보드에 복사</p>
<p> }</p>
<p>}</p>
<h3>&lt;b&gt;8.4.3 플러그인 배포 및 등록&lt;/b&gt;</h3>
<ul>
<li>프로젝트를 빌드하여 MyPlugin.dll 생성</li>
<li>.dll을 임의 폴더에 저장 (예: C:\\OLEDiPlugins\\MyPlugin.dll)</li>
<li>settings.json의 &quot;plugins&quot; 배열에 경로 등록:</li>
</ul>
<p>{ &quot;path&quot;: &quot;C:\\OLEDiPlugins\\MyPlugin.dll&quot;, &quot;enabled&quot;: true }</p>
<ul>
<li>OLEDi Commander 재시작 → 시스템 트레이 아이콘 우클릭 → &quot;플러그인 재로드&quot;</li>
</ul>
<table>
<tr><th><b>개발 팁: 핫 리로드</b><br/>개발 중에는 시스템 트레이 → "개발자 모드" 활성화 시 플러그인 파일 변경 감지 후 자동 재로드됩니다.<br/>OLEDiCommander.SDK.dll은 NuGet 패키지(OLEDiCommander.SDK)로 배포됩니다.<br/>단위 테스트 시 MockLauncherContext를 주입하여 UI 없이 IActionHandler를 테스트할 수 있습니다.</th></tr>
</table>
<h2>&lt;b&gt;8.5 확장 체크리스트&lt;/b&gt;</h2>
<p>새 기능을 추가하기 전에 아래 항목을 확인하십시오.</p>
<table>
<tr><th><b>체크 항목</b></th><th><b>확인 기준</b></th></tr>
<tr><td>Prefix 충돌 확인</td><td>기존 Prefix 테이블(섹션 2.2)과 중복 없는지 확인</td></tr>
<tr><td>에러 처리</td><td>네트워크 오류, 타임아웃, null 결과 등 모든 예외 처리 구현</td></tr>
<tr><td>CancellationToken 사용</td><td>사용자가 ESC 입력 시 진행 중인 비동기 작업 즉시 취소</td></tr>
<tr><td>캐싱 고려</td><td>API 호출 결과는 TTL 캐시 적용으로 불필요한 네트워크 요청 방지</td></tr>
<tr><td>로깅 추가</td><td>ILogger<T>를 통해 DEBUG/ERROR 레벨 로그 기록</td></tr>
<tr><td>단위 테스트</td><td>GetItemsAsync, ExecuteAsync 각각 최소 1개 이상 테스트 케이스 작성</td></tr>
<tr><td>아이콘 제공</td><td>32x32 PNG 아이콘을 LauncherItem.IconPath에 포함</td></tr>
<tr><td>설명 작성</td><td>PluginMetadata.Name, settings.json의 description 필드 작성</td></tr>
</table>
<p>&lt;br/&gt;</p>
<h1>&lt;b&gt;9. 설치 및 배포&lt;/b&gt;</h1>
<table>
<tr><th><b>항목</b></th><th><b>내용</b></th></tr>
<tr><td>배포 형태</td><td>MSIX 패키지 (Microsoft Store) 또는 Squirrel.Windows 인스톨러 (.exe)</td></tr>
<tr><td>Self-contained</td><td>.NET 8 Self-contained 배포 별도 런타임 설치 불필요</td></tr>
<tr><td>설치 경로</td><td>%LOCALAPPDATA%\Programs\OLEDiCommander\</td></tr>
<tr><td>설정 경로</td><td>%APPDATA%\OLEDiCommander\</td></tr>
<tr><td>자동 시작</td><td>HKCU Run 레지스트리 키 등록 (설치 시 옵션 선택)</td></tr>
<tr><td>자동 업데이트</td><td>앱 실행 시 GitHub Releases API 버전 확인, 백그라운드 다운로드</td></tr>
<tr><td>제거</td><td>일반 "앱 및 기능"으로 제거 가능. 설정 파일 유지 여부 선택</td></tr>
</table>
<h1>&lt;b&gt;10. 미결 사항 및 향후 계획 (v2.0)&lt;/b&gt;</h1>
<table>
<tr><th><b>항목</b></th><th><b>우선순위</b></th><th><b>비고</b></th></tr>
<tr><td>플러그인 마켓플레이스 UI</td><td>높음</td><td>서드파티 스킬 검색/설치/업데이트 통합 UI</td></tr>
<tr><td>OAuth 2.0 인증 흐름</td><td>높음</td><td>GitHub, Google 등 OAuth 기반 API 어댑터 지원</td></tr>
<tr><td>AI 자연어 명령</td><td>중간</td><td>예: "1주일 전 내 Jira 티켓 열어줘" → #jira 쿼리 자동 생성</td></tr>
<tr><td>클라우드 설정 동기화</td><td>중간</td><td>OneDrive / iCloud Drive를 통한 settings.json 동기화</td></tr>
<tr><td>플러그인 서명 강제화</td><td>높음</td><td>v2.0부터 서명 없는 .dll 실행 차단</td></tr>
<tr><td>스크립트 언어 지원</td><td>낮음</td><td>Python / Node.js 스크립트를 스킬로 직접 등록</td></tr>
<tr><td>음성 명령 입력</td><td>낮음</td><td>Windows Speech API 연동</td></tr>
</table>
<h1>&lt;b&gt;변경 이력&lt;/b&gt;</h1>
<table>
<tr><th><b>버전</b></th><th><b>날짜</b></th><th><b>작성자</b></th><th><b>변경 내용</b></th></tr>
<tr><td>v1.0</td><td>2026-03-21</td><td></td><td>초안 작성 (SRS 전체 구조 + 개발자 확장 가이드 포함)</td></tr>
</table>
<p class='footer'>Converted from: AXCommander_SRS_v1.0.docx</p>
</body></html>

39
AxCopilot.sln Normal file
View File

@@ -0,0 +1,39 @@
Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio Version 18
VisualStudioVersion = 18.2.11415.280 d18.0
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AxCopilot", "src\AxCopilot\AxCopilot.csproj", "{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AxCopilot.SDK", "src\AxCopilot.SDK\AxCopilot.SDK.csproj", "{B2C3D4E5-F6A7-8901-BCDE-F12345678901}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AxCopilot.Tests", "src\AxCopilot.Tests\AxCopilot.Tests.csproj", "{C3D4E5F6-A7B8-9012-CDEF-123456789012}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AxKeyEncryptor", "src\AxKeyEncryptor\AxKeyEncryptor.csproj", "{D4E5F6A7-B8C9-0123-DEFA-234567890123}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Debug|Any CPU.Build.0 = Debug|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.ActiveCfg = Release|Any CPU
{A1B2C3D4-E5F6-7890-ABCD-EF1234567890}.Release|Any CPU.Build.0 = Release|Any CPU
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B2C3D4E5-F6A7-8901-BCDE-F12345678901}.Release|Any CPU.Build.0 = Release|Any CPU
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C3D4E5F6-A7B8-9012-CDEF-123456789012}.Release|Any CPU.Build.0 = Release|Any CPU
{D4E5F6A7-B8C9-0123-DEFA-234567890123}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{D4E5F6A7-B8C9-0123-DEFA-234567890123}.Debug|Any CPU.Build.0 = Debug|Any CPU
{D4E5F6A7-B8C9-0123-DEFA-234567890123}.Release|Any CPU.ActiveCfg = Release|Any CPU
{D4E5F6A7-B8C9-0123-DEFA-234567890123}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

755
CLAUDE.md Normal file
View File

@@ -0,0 +1,755 @@
# AX Copilot 개발 지시사항
이 파일은 모든 개발 세션에서 일관된 품질의 결과물을 보장하기 위한 필수 지시사항입니다.
---
## 1. UI/UX 디자인 원칙
### 기본 컨트롤 사용 금지
- **ContextMenu, MenuItem** 사용 금지 → 커스텀 `Popup` (Border + MouseLeftButtonUp, 12px 라운드, 호버, 드롭섀도)
- **MessageBox** 사용 금지 → `CustomMessageBox.Show()` 사용
- **기본 CheckBox** 사용 금지 → `Style="{StaticResource ToggleSwitch}"` 좌우 슬라이드 토글
- **Popup 내부에 Button** 사용 금지 → `Border` + `MouseLeftButtonUp` 패턴 (포커스 캡처 방지)
- 수평 스크롤바 금지 → `WrapPanel` 자동 줄바꿈
### 테마 통일성
- 색상 하드코딩 금지 — XAML: `{DynamicResource PrimaryText}`, 코드비하인드: `TryFindResource("PrimaryText") as Brush ?? Brushes.White`
- 모든 UI 요소는 현재 적용된 테마 리소스(`LauncherBackground`, `ItemBackground`, `AccentColor`, `PrimaryText`, `SecondaryText` 등)를 사용
- 다크 테마 6종 모두에서 텍스트 가독성 확인 필수
### 코드비하인드 팝업/다이얼로그 테마 규칙
- **코드비하인드에서 생성하는 모든 팝업 Window/다이얼로그**도 현재 테마를 따라야 함
- 배경: `TryFindResource("LauncherBackground")`, 텍스트: `TryFindResource("PrimaryText")`, 보조 텍스트: `TryFindResource("SecondaryText")`
- 테두리: `TryFindResource("BorderColor")`, 아이템 배경: `TryFindResource("ItemBackground")`, 액센트: `TryFindResource("AccentColor")`
- 호버 효과 배경: `TryFindResource("ItemHoverBackground")`
- `#1A1B2E`, `Brushes.White` 등 고정 색상을 팝업에 직접 사용하는 것은 **금지** — 라이트/다크 테마 전환 시 색상 불일치 발생
- 팝업 Window에 테마 적용 패턴:
```csharp
// 팝업 생성 후 부모 창의 Resources를 팝업에 전달
popup.Resources.MergedDictionaries.Add(this.Resources);
// 또는 직접 리소스 조회
var bg = TryFindResource("LauncherBackground") as Brush ?? Brushes.White;
var fg = TryFindResource("PrimaryText") as Brush ?? Brushes.Black;
```
### 인터랙티브 요소
- 클릭 가능 영역 최소 36px, FontSize 최소 12px, Padding 최소 6px
- 호버/클릭 효과 필수 — `#18FFFFFF` 반투명 배경 + 핸드 커서
- 팝업은 `PopupAnimation="Fade"` 기본 적용
- 메뉴 항목 FontSize 13px 이상
### 아이콘 색상
- Segoe MDL2 Assets 아이콘은 가능한 한 **의미에 맞는 색상**을 적용 (단색 `SecondaryText` 지양)
- 메뉴 항목: 아이콘 + 라벨에 동일 색상 적용 (예: Ask=파랑, Auto=앰버, Deny=빨강)
- 하단 바: 각 버튼 아이콘에 기능별 색상 (포맷=보라, 파일=앰버, 권한=파랑, 데이터=녹색)
- 활성 상태 항목: `AccentColor` 또는 해당 기능 고유 색상으로 강조
### 설정 UI 패턴
- 켜기/끄기: ToggleSwitch 스타일 (Grid 좌: 라벨, 우: 토글)
- 선택형: 커스텀 Popup 드롭다운 (`[라벨: 현재값 ▾]`)
- AI/고급 설정 항목 옆에 `?` 도움말 아이콘 + 커스텀 다크 툴팁 (`HelpTooltipStyle`)
- 설정 저장 시 `CustomMessageBox`로 완료 알림
### 헬프 화면 (HelpDetailWindow) 규칙
- **버전 정보 금지** — 헬프에 버전별 신기능(예: "v1.6.0 신기능") 항목을 넣지 않으며, 앞으로도 추가하지 않음
- **영역별 핵심 기능만 표시** — 개요, AI, 업무 보조 등 영역별로 사용자가 "무엇을 할 수 있는지"만 간결히 설명
- **기술 보호 (IP 보호)** — 설명에서 내부 기술·구현 방식을 유추할 수 없어야 함. 아래 용어를 헬프에 직접 노출하는 것은 **금지**:
| 사용 금지 (내부 기술) | 대신 사용 (사용자 관점) |
|---------------------|----------------------|
| SSE, Server-Sent Events | "실시간 응답" |
| AES-256-GCM, DPAPI, 암호화 알고리즘명 | "암호화 저장", "안전하게 보호" |
| OpenXML, python-docx, openpyxl | "문서 생성", "파일 생성" |
| MCP, JSON-RPC, stdio | "외부 도구 연결" |
| LINQ, TF-IDF, LCS, Mustache | 기능 설명으로 대체 |
| Ollama, vLLM, Gemini, Claude (모델명) | "AI 서비스", "AI 모델" |
| OWASP, CVE | "보안 점검", "취약점 분석" |
| system_prompt.txt, 클래스명, 파일명 | 기능 설명으로 대체 |
---
## 2. 설정값 관리 원칙
### 신규 기능 → 설정 검토 필수
신규 기능 추가 시 사용자/개발자가 제어할 수 있는 설정값을 반드시 검토하고 추가합니다:
1. `AppSettings.cs`에 프로퍼티 + JsonPropertyName + 기본값 추가
2. `SettingsViewModel.cs`에 바인딩 프로퍼티 + Load/Save 매핑
3. `SettingsWindow.xaml`에 적절한 탭에 UI 컨트롤 배치
4. 도구(Tool) 클래스에서 설정값을 실제로 체크하여 동작에 반영
### Cowork / Code 설정 분리 원칙
에이전트 동작 설정은 **공통(LlmSettings)에 넣지 말고, Cowork/Code 각각에 배치**합니다:
- **Cowork 전용 설정**: `LlmSettings`에 직접 프로퍼티 추가 → SettingsWindow의 `AgentPanelCowork` 패널에 배치
- **Code 전용 설정**: `CodeSettings` 클래스에 프로퍼티 추가 → SettingsWindow의 `AgentPanelCode` 패널에 배치. XAML 바인딩은 `{Binding Code.PropertyName}`
- **진짜 공통인 경우만** `AgentPanelCoworkCode` 패널에 배치 (예: MaxAgentIterations, MaxRetryOnError)
- 검증 강제, 출력 형식, 폴더 데이터 활용 등 **탭마다 다르게 동작하는 기능**은 반드시 분리
- AgentLoopService에서 `ActiveTab == "Code"` 분기로 탭별 설정 참조
```csharp
// 탭별 설정 참조 패턴
var shouldVerify = ActiveTab == "Code"
? llm.Code.EnableCodeVerification
: llm.EnableCoworkVerification;
```
### 설정값이 코드에서 실제 동작해야 함
설정을 정의만 하고 코드에서 읽지 않는 것은 금지. 설정 체크 패턴:
```csharp
var app = System.Windows.Application.Current as App;
var enabled = app?.SettingsService?.Settings.Llm.Code.EnableLsp ?? true;
if (!enabled) return ToolResult.Ok("비활성 상태입니다. 설정에서 활성화하세요.");
```
---
## 3. 버전 관리 및 배포
### 버전 번호 규칙
- **소규모 배포** (버그 수정, 설정 추가, UI 개선): `+0.0.1` (예: 1.4.0 → 1.4.1)
- **대규모 기능 배포** (Phase 완료, 새 에이전트 도구, 핵심 기능): `+0.1.0` (예: 1.4.0 → 1.5.0)
### 버전 변경 시 반드시 수정할 파일 (체크리스트)
| # | 대상 | 파일 경로 | 수정 내용 |
|---|------|----------|----------|
| 1 | 앱 버전 | `src/AxCopilot/AxCopilot.csproj``<Version>` | 버전 번호 |
| 2 | 인스톨러 프로젝트 | `src/AxCopilot.Installer/AxCopilot.Installer.csproj``<Version>` | 동일 |
| 3 | 인스톨러 표시 | `src/AxCopilot.Installer/SetupForm.cs``AppVer` | 동일 |
| 4 | MCP 클라이언트 | `src/AxCopilot/Services/McpClientService.cs``clientInfo.version` | 동일 |
| 5 | 개발자 가이드 | `src/AxCopilot/Assets/AX Copilot 개발자가이드.htm` | 버전 이력 항목 추가만 (**헤더/푸터에 버전 번호 표기 금지**) |
| 6 | 사용자 가이드 | `src/AxCopilot/Assets/AX Copilot 사용가이드.htm` | 개발자 가이드에서 버전이력 섹션 제거 후 복사 (**헤더/푸터 버전 표기 없음**) |
| 7 | 가이드 암호화 | `src/AxCopilot/Assets/guide_dev.enc`, `guide_user.enc` | 가이드 수정 후 반드시 암호화 재실행 |
| 8 | 개발 문서 | `docs/DEVELOPMENT.md` | 버전 이력 추가 |
| 9 | 로드맵 문서 | `docs/AGENT_ROADMAP.md/html`, `docs/LAUNCHER_ROADMAP.md/html` | 버전 번호 갱신 |
### 가이드 문서 관리 워크플로우
가이드 문서는 **개발자 가이드가 마스터**이며, 사용자 가이드는 이를 기반으로 생성합니다:
1. **개발자 가이드 수정** (`AX Copilot 개발자가이드.htm`) — 버전 이력 항목 추가. **헤더 `version-tag` div 및 푸터 버전 번호는 표기하지 않음**
2. **사용자 가이드 생성** — 개발자 가이드를 복사한 뒤 **버전 이력 섹션 제거** (`<!-- 버전 이력 -->` ~ `</div><!-- /.version-section -->`). 헤더/푸터에 버전 번호 없음
3. **암호화 실행**`encrypt_guides.ps1` 스크립트 실행하여 `.enc` 파일 생성
4. **빌드**`.enc` 파일이 출력 폴더에 복사됨 (평문 .htm은 출력에 포함되지 않음)
```bash
# 가이드 암호화 명령 (프로젝트 루트에서)
powershell -ExecutionPolicy Bypass -File encrypt_guides.ps1
```
- 앱에서는 `GuideViewerWindow`가 암호화된 가이드를 복호화하여 내장 뷰어로 표시
- 개발자 모드 ON → 개발자 가이드 (버전이력 포함), OFF → 사용자 가이드 (버전이력 없음)
- 암호화 키: `GuideEncryptor.cs`에 고정 AES-256-CBC 키 내장 (모든 PC 동일)
### 사용자 노출 문서 작성 원칙 (사용가이드 · 헬프)
사용자 가이드와 헬프 화면은 **내부 기술이 노출되지 않도록** 작성합니다:
| 구분 | 사용 금지 (내부 기술) | 대신 사용 (사용자 관점) |
|------|---------------------|----------------------|
| 프로토콜 | MCP, JSON-RPC, stdio, P/Invoke, DPAPI | "외부 도구 연결", "암호화 저장" |
| 클래스명 | McpClientService, TokenEstimator, DiffService | 기능 설명으로 대체 |
| 내부 구조 | FallbackModels, SettingsViewModel, ParentId | "자동 전환", "설정", "분기" |
| 코드 패턴 | CJK 가중치, SWE-bench, LCS, TF-IDF | "더 정확한 분석", "검색 개선" |
**원칙**: 사용자에게는 "무엇을 할 수 있는지"만 전달합니다.
---
## 4. 코드 품질
### 빌드 기준
- 모든 변경 후 `dotnet build` 실행 → **경고 0, 오류 0** 필수
- CS8603 (nullable) 경고 즉시 수정
### 리소스 관리
- `IDisposable` 구현 객체는 반드시 해제 (PerformanceCounter, LspClientService 등)
- P/Invoke 메모리: `Marshal.AllocHGlobal``finally`에서 `FreeHGlobal`
- WinEvent 훅: `UnhookWinEvent` 보장
### 에이전트 도구 등록
-`IAgentTool` 구현 시 `ToolRegistry.CreateDefault()``Register()` 추가 필수
- 도구의 `Parameters` 스키마가 LLM function calling 명세에 정확히 맞는지 확인
---
## 5. AI 차단 버전 (클로드 버전) 패턴
AI 기능이 필요 없는 환경에 배포하거나, AI 기능 문의를 차단해야 할 때 사용하는 패턴입니다.
### 활성화/비활성화 제어 방법
`AppSettings.AiEnabled` (기본값 **`false`**) 한 곳만 바꾸면 전체 AI 기능이 제어됩니다.
| 조건 | 동작 |
|------|------|
| `AiEnabled = false` | **기본값 (배포 기본)** — AI 전체 차단 |
| `AiEnabled = true` | AI 활성화 — 비밀번호 인증 후 설정 가능 |
### AI 활성화 비밀번호
- 설정 창 > 일반 탭 > AI 기능 토글 ON 시 비밀번호 다이얼로그 표시
- 비밀번호: **`axgo123!`**
- 비밀번호 틀리거나 취소 시 토글 자동 복구 (OFF 유지)
- 비활성화(OFF)는 비밀번호 없이 즉시 적용
- 구현 위치: `SettingsWindow.xaml.cs` `AiEnabled_Changed()`
### AI 차단 시 적용되는 항목
| 항목 | 구현 위치 | 설명 |
|------|----------|------|
| `!` prefix 배지 숨김 | `LauncherViewModel.cs` `ActivePrefix`/`HasActivePrefix` | `!` prefix 인식 안 함 |
| `!` 입력 시 결과 없음 | `ChatHandler.cs` `GetItemsAsync` | 빈 리스트 반환 (항목 자체 미표시) |
| `!` 실행 차단 | `ChatHandler.cs` `ExecuteAsync` | 실행 불가 |
| 트레이 메뉴 항목 숨김 | `App.xaml.cs` `Opening` 이벤트 | "AX Agent 대화하기" 항목 `Collapsed` |
| AX Agent 설정 탭 숨김 | `SettingsWindow.xaml.cs` `ApplyAiEnabledState()` | 설정 창의 AX Agent 탭 `Collapsed` |
### 설정 토글 UI
- 위치: 설정 창 > 일반 탭 > **AI 기능** 섹션 > "AX Agent (AI 기능) 활성화" 토글
- 저장 위치: `%APPDATA%\AxCopilot\settings.dat``"ai_enabled": true/false`
### 신규 AI 기능 추가 시 체크리스트
AI 관련 기능을 새로 추가하면 `AiEnabled` 체크를 반드시 연동합니다:
```csharp
// 핸들러/도구에서 AI 차단 체크 패턴
var settings = (System.Windows.Application.Current as App)?.SettingsService?.Settings;
if (settings?.AiEnabled == false) return; // 또는 빈 결과 반환
```
---
## 6. 사내/사외 모드 (Network Access Mode)
### 개요
배포 환경에 따라 외부 인터넷 접속 허용 여부를 제어합니다.
| 구분 | 모드 이름 | 기본값 | 설명 |
|------|----------|--------|------|
| `InternalModeEnabled = true` | **사내 모드** | ✅ 기본 | 외부 인터넷 접속 차단. 사내망 + LLM API만 허용 |
| `InternalModeEnabled = false` | **사외 모드** | — | 인터넷 검색, 외부 HTTP 허용 (비밀번호 인증 필요) |
### 제어 방법
`AppSettings.InternalModeEnabled` (기본값 **`true`**) 한 곳만 바꾸면 모든 외부 접속 제어가 변경됩니다.
- 설정 창 > 일반 탭 > **네트워크 모드** 섹션 > "사외 모드 활성화" 토글
- 사외 모드 ON 시 비밀번호 다이얼로그 표시 → **`axgo123!`**
- 사내 모드 복귀(OFF)는 비밀번호 없이 즉시 적용
### 모드별 허용/차단 항목
| 기능 | 사내 모드 | 사외 모드 |
|------|---------|---------|
| LLM API 호출 (Ollama/Gemini/Claude) | ✅ 허용 | ✅ 허용 |
| 사내 MCP 서버 연결 | ✅ 허용 | ✅ 허용 |
| 웹 검색 (? 프리픽스) | ❌ 차단 | ✅ 허용 |
| `http_request` 도구 외부 URL | ❌ 차단 | ✅ 허용 |
| 외부 MCP 서버 연결 | ❌ 차단 | ✅ 허용 (명시적 등록 시) |
### 코드 체크 패턴
```csharp
// 외부 접속이 필요한 기능에서 반드시 체크
var settings = (System.Windows.Application.Current as App)?.SettingsService?.Settings;
if (settings?.InternalModeEnabled == true)
return ToolResult.Fail("사내 모드에서는 외부 인터넷 접속이 차단됩니다. 설정에서 사외 모드를 활성화하세요.");
// URL이 내부 주소인지 검사 (사내 모드에서 허용 가능한 경우)
bool IsInternalUrl(string url) =>
url.StartsWith("http://localhost", StringComparison.OrdinalIgnoreCase) ||
url.StartsWith("http://127.", StringComparison.OrdinalIgnoreCase) ||
url.StartsWith("http://192.168.", StringComparison.OrdinalIgnoreCase) ||
url.StartsWith("http://10.", StringComparison.OrdinalIgnoreCase) ||
url.StartsWith("http://172.", StringComparison.OrdinalIgnoreCase);
```
### 신규 외부 접속 기능 추가 시 체크리스트
외부 인터넷에 접속하는 기능을 추가하면 반드시 `InternalModeEnabled` 체크를 연동합니다:
1. 도구 또는 핸들러 실행 초반에 모드 체크
2. 사내 모드에서는 명확한 오류 메시지 반환
3. SettingsWindow에 해당 기능의 모드 의존성 설명 추가
---
## 7. 공통 비밀번호 정책
앱 내 보호된 설정은 모두 동일한 비밀번호를 사용합니다:
| 보호 항목 | 비밀번호 | 구현 위치 |
|----------|---------|---------|
| AI 기능 활성화 | `axgo123!` | `SettingsWindow.xaml.cs` `AiEnabled_Changed()` |
| 사외 모드 활성화 | `axgo123!` | `SettingsWindow.xaml.cs` `NetworkMode_Changed()` |
**구현 규칙**:
- 비밀번호를 코드에 하드코딩 — 암호화 불필요 (배포 제어용, 보안 목적 아님)
- 상수로 분리: `private const string SettingsPassword = "axgo123!";`
- 비밀번호 일치 시만 토글 활성화 유지, 불일치/취소 시 토글 자동 복구
---
## 8. 프로젝트 명칭 체계
| 구분 | 명칭 | 용도 |
|------|------|------|
| 앱 전체 | **AX Copilot** | 제품명, 설정, 정보 |
| 런처 | **AX Commander** | 명령 입력창 |
| AI 대화 | **AX Agent** | Chat/Cowork/Code 대화 |
---
## 9. 사내 환경 개발 원칙
### 외부 의존 최소화 (항상 적용)
- **데이터 로컬 저장** — 설정, 대화, 클립보드, 인덱스, 로그 등 모든 데이터는 `%APPDATA%\AxCopilot\` 로컬에 저장. 클라우드 동기화 없음
- **플러그인 설치** — URL 기반 다운로드 금지. 로컬 zip 파일 기반 설치만 허용
- **코드 검색/인덱싱** — 외부 임베딩 API 사용 금지. 로컬 TF-IDF 또는 로컬 임베딩 엔진(ONNX) 사용
- **업데이트 확인** — 외부 서버 자동 업데이트 체크 금지. 인스톨러를 통한 수동 업그레이드만 지원
- **텔레메트리** — 사용 통계를 외부로 전송하지 않음. 모든 통계는 로컬 파일에만 기록
### 항상 허용되는 외부 접속
- LLM API 호출 (Ollama/vLLM은 사내 서버, Gemini/Claude는 설정에 따라)
- 사내 MCP 서버 연결 (설정에서 명시적 등록된 것만)
### 사외 모드에서만 허용 (섹션 6 참조)
- 인터넷 웹 검색 (? 프리픽스)
- `http_request` 도구의 외부 URL 호출
- 외부 MCP 서버 연결
---
## 10. 문서 관리 원칙
### 기본 원칙: .md 파일이 마스터
- **모든 로드맵·계획 문서는 `.md` 파일로 관리**합니다. 기본 작업(기능 개발·계획 수립·완료 기록)은 항상 `.md` 파일만 업데이트합니다.
- **HTML 웹 문서는 명시적으로 요청할 때만 업데이트**합니다. ("웹 문서도 업데이트해줘" 또는 "HTML 문서 갱신해줘" 요청 시에만 작업)
- HTML 문서는 `.md` 마스터를 기반으로 생성되는 **파생 문서**입니다. 자동으로 동기화하지 않습니다.
### 문서 체계
| 문서 (.md 마스터) | HTML (파생) | 관리 대상 | .md 업데이트 시점 |
|-----------------|------------|----------|-----------------|
| `docs/AGENT_ROADMAP.md` | `docs/AGENT_ROADMAP.html` | **대화 서비스** Phase별 기능, 완료 이력, 기술부채 | Phase 완료 시, 기능 추가 시 |
| `docs/LAUNCHER_ROADMAP.md` | `docs/LAUNCHER_ROADMAP.html` | **런처** Phase별 기능, 완료 이력, 경쟁 비교 | Phase 완료 시, 기능 추가 시 |
| `docs/NEXT_ROADMAP.md` | `docs/NEXT_ROADMAP.html` | **종합** 경쟁 분석, 기술 동향, 차기 계획 | 분기별 또는 대규모 계획 변경 시 |
| `docs/DEVELOPMENT.md` | — | **개발 상세** 아키텍처, 핸들러, 버전 이력, 코드 패턴 | 매 버전 배포 시 |
### 문서 업데이트 규칙
- **기능 개발 완료 시**: 해당 영역 .md 로드맵(AGENT/LAUNCHER)에 완료 표시 + 구현 내용 기록
- **배포 시**: DEVELOPMENT.md 버전 이력 추가, 사용자 가이드/헬프 갱신
- **차기 계획 수립 시**: NEXT_ROADMAP.md 업데이트 (경쟁 분석 반영)
- **HTML 동기화 요청 시에만**: 해당 .md 내용을 기반으로 HTML 파일 갱신 (열고/닫기 토글, badge 스타일 done/plan/hold 통일)
---
## 11. 고도화 계획 수립 원칙
### 외부 동향 기반 계획 수립
고도화 계획(Phase)을 수립할 때는 **내부 개발 필요성만이 아닌 외부 동향**을 종합적으로 반영합니다:
| 관점 | 검토 사항 |
|------|----------|
| **경쟁 서비스** | Claude Code, Cursor, Windsurf, GitHub Copilot, Raycast 등 최신 기능/UX 비교 |
| **최신 논문/기술** | Agentic Coding Survey, SWE-Agent, CodeAct, Reflexion 등 에이전트 코딩 연구 동향 |
| **업계 표준** | MCP 프로토콜, SKILL.md 오픈 포맷, LSP, DAP 등 표준 프로토콜 채택 여부 |
| **사내 환경** | 네트워크 제한, 보안 정책, Python/Node 설치 현황, 사용 빈도가 높은 워크플로우 |
| **커뮤니티 스킬** | Anthropic 공식 스킬, 오픈소스 프롬프트 엔지니어링 기법, 검증된 에이전트 패턴 |
### 앱 크기 관리
배포 앱(인스톨러)의 크기가 과도하게 커지지 않도록 관리합니다:
- **기본 배포 크기 목표**: 인스톨러 **150MB 이하** 유지 (현재 ~80MB)
- **대형 의존성 추가 시**: ONNX 모델, 임베딩 엔진, 사전 데이터 등은 **별도 선택적 다운로드** 또는 **로컬 zip 설치** 방식 검토
- **스킬 파일**: 내장 스킬(`.skill.md`)은 텍스트 기반이므로 크기 부담 없음 (수십 KB 단위). 적극 번들 가능
- **NuGet 패키지**: 새 패키지 추가 시 DLL 크기 확인. 단일 기능에 10MB+ 패키지는 대안 검토
- **런타임 의존**: Python/Node 스크립트 기반 기능은 사용자 PC 런타임에 의존 → 앱 크기 증가 없음
- **리소스 파일**: 이미지/아이콘은 SVG 또는 시스템 폰트(Segoe MDL2 Assets) 우선. 대형 비트맵 금지
---
## 12. 코드 설계 원칙 (SOLID + 디자인 패턴)
### SOLID 원칙 준수 (필수)
#### S — 단일 책임 원칙 (SRP)
- **클래스 하나 = 책임 하나**. 에이전트 도구 클래스는 도구 로직만, UI 코드비하인드는 UI만 담당
- 500줄 이상 클래스는 분리 검토. 메서드는 20줄 이하 권장
- 파일 I/O, 비즈니스 로직, UI 렌더링이 같은 클래스에 섞이는 것 금지
```csharp
// 금지: AgentLoopService가 UI도 업데이트
// 허용: AgentLoopService → 이벤트 발행 → ViewModel이 UI 갱신
```
#### O — 개방/폐쇄 원칙 (OCP)
- 새 도구 추가 시 기존 `AgentLoopService` 수정 없이 `IAgentTool` 구현 + Registry 등록만으로 확장
- 새 훅 이벤트 추가 시 기존 훅 처리기 수정 없이 새 핸들러 등록으로 확장
- `switch/if-else` 체인으로 타입 분기하는 것 지양 → 다형성 또는 전략 패턴으로 대체
#### L — 리스코프 치환 원칙 (LSP)
- `IAgentTool` 구현체는 항상 교환 가능해야 함 — 특정 구현에만 의존하는 코드 금지
- `ToolResult.Ok()` / `ToolResult.Error()` 반환 규약은 모든 도구 구현체에서 일관 유지
#### I — 인터페이스 분리 원칙 (ISP)
- 거대 인터페이스 금지. `IAgentTool``Execute`, `Parameters`, `Name`만 정의
- 추가 기능(검증, 로깅, 캐시)은 별도 인터페이스(`IVerifiable`, `IAuditable`)로 분리
- 구현체가 사용하지 않는 메서드를 강제하는 인터페이스 금지
#### D — 의존성 역전 원칙 (DIP)
- 구체 클래스가 아닌 **인터페이스/추상 클래스에 의존**
- `AgentLoopService``IAgentTool[]`, `ILlmService`, `IHookRunner`에만 의존 (구체 구현 직접 참조 금지)
- 의존성 주입: 생성자 주입 우선. `App.SettingsService`처럼 전역 싱글턴 직접 접근은 서비스 계층에만 허용
### 적극 사용할 디자인 패턴
#### Strategy 패턴 — 에이전트 도구, 훅 타입, 권한 모드
```csharp
// 훅 타입별 전략 분리
public interface IHookExecutor { Task<HookResult> ExecuteAsync(HookContext ctx); }
public class CommandHookExecutor : IHookExecutor { ... }
public class PromptHookExecutor : IHookExecutor { ... } // NEW: LLM 검사
public class AgentHookExecutor : IHookExecutor { ... } // NEW: 에이전트 루프
```
#### Observer 패턴 — 에이전트 이벤트, 훅 이벤트
```csharp
// AgentLoopService에서 이벤트 발행 → 다수 구독자 (UI, 훅, 감사 로그)
public event EventHandler<AgentEvent> AgentEventOccurred;
// 구독: WorkflowAnalyzer, HookRunner, AuditLogService 각각 독립 구독
```
#### Factory 패턴 — 도구 생성, 훅 생성, 에이전트 생성
```csharp
// 도구 팩토리: 타입 기반 생성, 외부에서 new 금지
public static class AgentToolFactory
{
public static IAgentTool Create(string toolName, LlmSettings settings) { ... }
}
```
#### Decorator 패턴 — 도구 검증·로깅·위험도 래핑
```csharp
// 기존 도구에 검증/로깅 기능 추가 — 도구 자체 수정 없이
public class VerifiedToolDecorator : IAgentTool
{
private readonly IAgentTool _inner;
public VerifiedToolDecorator(IAgentTool inner) { _inner = inner; }
public async Task<ToolResult> ExecuteAsync(...)
{
var result = await _inner.ExecuteAsync(...);
await RunPostVerificationAsync(result);
return result;
}
}
```
#### Chain of Responsibility — 훅 체인, 권한 규칙 체인
```csharp
// 권한 규칙을 체인으로 처리: Allow규칙 → Deny규칙 → 모드 기본값
public abstract class PermissionRule
{
protected PermissionRule? _next;
public abstract PermissionDecision Evaluate(ToolCall call);
}
```
#### Repository 패턴 — 설정, 대화 이력, 에이전트 메모리
```csharp
public interface IAgentMemoryRepository
{
Task<AgentMemory?> GetAsync(string agentType, string projectId);
Task SaveAsync(AgentMemory memory);
}
```
#### Command 패턴 — 슬래시 명령, 커맨드 팔레트
```csharp
public interface ISlashCommand
{
string Name { get; }
string Description { get; }
Task ExecuteAsync(string arguments, IChatContext context);
}
```
### 구조 분리 원칙
#### 계층 분리 규칙
```
UI 계층 (Views/ViewModels)
↓ 이벤트/명령 (Commands, Events)
서비스 계층 (Services)
↓ 인터페이스 (IAgentTool, IHookExecutor 등)
도메인 계층 (Models, Tools)
↓ 데이터 접근 (Repositories)
인프라 계층 (외부 API, 파일 시스템)
```
- **계층 역방향 의존 금지**: 도메인 계층이 UI를 참조하는 것 금지
- **서비스 계층 간 직접 호출 지양**: 이벤트/메시지 버스로 느슨한 결합
#### 에이전트 도구 분리 원칙
- 도구 클래스는 `Services/AgentTools/` 하위의 **카테고리별 폴더**에 배치
- 각 도구는 `독립 파일` 1개 — 여러 도구를 하나의 파일에 합치는 것 금지
- 도구 간 직접 호출 금지 — 공통 로직은 `Services/AgentToolHelpers/`에 분리
#### 설정 분리 원칙 (기존 원칙 강화)
- Cowork/Code 전용 설정은 반드시 분리 (공통 설정에 넣지 않음)
- 새 기능 추가 시 설정 클래스 → ViewModel → UI → 코드 순서로 구현
### 새 기능 구현 체크리스트
새 Phase 기능을 구현하기 전 확인:
1. **인터페이스 먼저**: 구체 구현 전 `IXxx` 인터페이스 정의
2. **단일 책임**: 클래스가 2가지 이상의 이유로 변경될 수 있으면 분리
3. **패턴 적합성**: Strategy/Observer/Decorator 중 적합한 패턴 선택
4. **의존성 방향**: 새 클래스의 의존성이 올바른 방향인지 확인
5. **설정 연동**: 설정 없이 하드코딩된 동작 금지
6. **테스트 가능성**: 외부 의존(파일, API)을 인터페이스로 추상화하여 단위 테스트 가능하게
---
## 13. AX Agent 채팅 화면 UI/UX 원칙 (Claude.ai + Codex 스타일)
### 핵심 패러다임: AX Agent = 독립 앱 공간
**절대 원칙**: AX Agent와 관련된 **모든** 설정은 ChatWindow 내부에서 처리한다.
- SettingsWindow에서 **AX Agent 탭을 완전 제거**. AX Agent 관련 설정은 ChatWindow 내 설정 패널로 이관.
- 사용자는 ChatWindow를 벗어나지 않고 모델 변경, 도구 토글, 권한 모드, 에이전트 파라미터, API 키까지 모두 설정 가능해야 함.
- SettingsWindow에는 앱 전역 설정(테마, 핫키, 런처, 인덱싱)만 남긴다.
> **레퍼런스**: Claude.ai — 대화 목록 + 채팅 + 설정이 단일 창 내 패널로 구성. Codex — 에이전트 제어 항목(모델, 도구, 권한, Plan) 전부 채팅 헤더/사이드바에 인라인 배치. 별도 설정 다이얼로그 없음.
---
### 전체 레이아웃 구조 (3-Pane)
```
┌──────────────────────────────────────────────────────────────────────┐
│ [좌측 사이드바 260px] │ [메인 채팅 영역 — 가변] │ [우측 패널 300px] │
│ │ │ (토글, 기본 접힘) │
│ ┌──────────────────┐ │ ┌─────────────────────┐ │ │
│ │ ≡ AX Agent ✕ │ │ │ 현재 탭 이름 │ │ ┌───────────────┐ │
│ └──────────────────┘ │ │ 모델▾ Plan▾ 권한▾ │ │ │ ⚙ 설정 패널 │ │
│ │ └─────────────────────┘ │ │ ───────────── │ │
│ [ Chat │ Cowork│Code] │ │ │ 탭별 에이전트 │ │
│ ──────────────────── │ [메시지 스트림] │ │ 파라미터 │ │
│ ▸ 프리셋1 │ │ │ 모델/API 설정 │ │
│ ▸ 프리셋2 │ │ │ 도구 토글 │ │
│ ▸ 프리셋3 │ │ │ MCP 서버 │ │
│ ──────────────────── │ │ └───────────────┘ │
│ [+ 새 대화] │ │ │
│ ──────────────────── │ ┌──────────────────────┐ │ ┌───────────────┐ │
│ 오늘 │ │ @멘션 /스킬 📎 ⚙ │ │ │ 🔀 워크플로우 │ │
│ · 대화 제목 1 │ │ [ 입력 텍스트 ] │ │ └───────────────┘ │
│ · 대화 제목 2 │ │ 모델칩 권한칩 [전송]│ │ ┌───────────────┐ │
│ 어제 │ └──────────────────────┘ │ │ 📄 Diff 뷰 │ │
│ · 대화 제목 3 │ │ └───────────────┘ │
└──────────────────────────────────────────────────────────────────────┘
```
---
### 좌측 사이드바 (AgentSidebarView) — Claude.ai 스타일
#### 구조 및 동작
- **헤더**: `≡ AX Agent` 로고 + 사이드바 접기 버튼 (최소 48px 아이콘 모드로 접힘)
- **탭 세그먼트**: `Chat | Cowork | Code` — 가로 3분할 세그먼트 컨트롤 (Border + MouseLeftButtonUp, 선택 탭 AccentColor 배경)
- **프리셋 목록**: 현재 탭의 프리셋 항목 직접 목록 표시. 클릭 시 즉시 적용. 우클릭 커스텀 Popup (편집/삭제)
- **새 대화 버튼**: `[ 새 대화]` — 전체 너비 Border 버튼, 호버 시 AccentColor 틴트
- **대화 이력**: 날짜 그룹(`오늘 / 어제 / 이전 7일 / 이전 30일`) 구분선 + 대화명. 클릭 시 해당 세션 복원. 우클릭 커스텀 Popup (이름변경/삭제/즐겨찾기)
- **하단**: ⚙ 설정 아이콘 → 우측 설정 패널 토글 (Claude.ai 프로필 메뉴 대응)
#### 크기/테마
- 기본 너비 260px, 접기 시 48px (아이콘만)
- 배경: `LauncherBackground`, 구분선: `BorderColor` 1px, 선택 항목: `ItemHoverBackground`
- 접기/펼치기: Width 애니메이션 260→48px, 180ms EaseInOut
---
### 메인 채팅 영역 — 탭별 콘텐츠
#### 세션 헤더 바 (탭당 고정)
탭 전환 시 해당 탭의 헤더로 교체됨. 항상 표시.
| 위치 | 컨트롤 | 구현 | 설명 |
|------|--------|------|------|
| 좌측 | 현재 탭 레이블 | TextBlock | "Chat", "Cowork", "Code" |
| 중앙 | 모델 선택칩 | Border + Popup | 현재 모델명 + ▾. 클릭 시 등록 모델 목록 팝업 |
| 중앙 | Plan 모드 칩 | Border 3-state | Off → Auto → Always 순환 클릭. 활성=AccentColor |
| 중앙 | 권한 모드 칩 | Border + Popup | Default / AcceptEdits / Plan / Bypass |
| 우측 | 도구 아이콘 열 | 아이콘 N개 | 활성=기능색, 비활성=SecondaryText 흐림. 클릭 토글 |
| 우측 | ⚙ 설정 버튼 | Border | 우측 설정 패널 슬라이드인 토글 |
| 우측 | 🔀 워크플로우 | Border | 워크플로우 분석기 우측 패널 열기 |
#### 메시지 스트림
- 사용자 메시지: 우측 정렬, `ItemBackground` 배경 버블, 최대 너비 70%
- AI 메시지: 좌측 정렬, 배경 없음 (Claude.ai 스타일), 마크다운 렌더링
- 도구 호출 블록: 접히는 `Border` (`▶ file_write` → 클릭 시 펼쳐서 파라미터/결과 표시)
- 스트리밍 중: 커서 깜빡임 애니메이션 (Opacity 0→1, 500ms 반복)
- 신규 메시지 SlideIn: TranslateTransform Y +20→0, Opacity 0→1, 120ms EaseOut
---
### 우측 설정 패널 (AgentSettingsPanel) — 필수 구현
**⚙ 클릭 시 우측에서 슬라이드인 (TranslateTransform X +300→0, 200ms EaseOut)**
**기존 SettingsWindow의 AX Agent 탭을 이 패널로 완전 대체.**
#### 패널 구성 (탭별 섹션 분기)
```
┌─────────────────── ⚙ 설정 ────── [✕] ┐
│ ── 현재 탭: Chat / Cowork / Code ── │
│ │
│ [모델 & 서비스] │
│ LLM 서비스 [Ollama ▾] │
│ 모델 [llama3:8b ▾] │
│ API 엔드포인트 [____________] │
│ API 키 [•••••••• 👁] │
│ │
│ [에이전트 동작] │
│ 최대 반복 횟수 [──────○──] 20 │
│ 오류 시 재시도 [──○──────] 3 │
│ 병렬 도구 실행 ◉──── (토글) │
│ │
│ [탭 전용 설정] ← 탭에 따라 다름 │
│ (Cowork) 검증 강제 ◉──── │
│ (Code) LSP 분석 ◉──── │
│ (Code) 작업 폴더 [경로... 📁] │
│ │
│ [도구 관리] │
│ file_write ◉──── 활성 │
│ file_read ◉──── 활성 │
│ git_tool ────◉ 비활성 │
│ … │
│ │
│ [MCP 서버] │
│ ● server1 연결됨 [비활성화] │
│ ○ server2 오프라인 [재연결] │
│ │
│ [프리셋] │
│ 현재 설정으로 저장 [저장] │
│ 프리셋 관리 [관리 →] │
│ │
│ [고급] │
│ 폴백 모델 목록 [편집] │
│ 프로젝트 규칙 ◉──── 활성 │
│ AX.md 편집 [열기 →] │
└───────────────────────────────────────┘
```
#### 설정 패널 구현 규칙
- 모든 컨트롤: ToggleSwitch 또는 Border+Popup 드롭다운 (Section 1 원칙 준수)
- API 키 필드: `PasswordBox` 스타일 + 👁 토글로 표시/숨김
- 탭별 분기: `ActiveTab == "Code"` 조건으로 탭 전용 섹션 표시/숨김
- 설정 변경 즉시 저장 (`SettingsService.Save()`) — 별도 저장 버튼 불필요
- 슬라이더: WPF `Slider` + 현재값 TextBlock 우측 표시
---
### SettingsWindow 잔류 원칙 (AX Agent 탭 제거)
SettingsWindow에 **남기는 항목** (앱 전역 설정만):
- **일반**: AI 활성화(비밀번호), 네트워크 모드(비밀번호), 앱 시작 설정, 개발자 모드
- **런처**: 인덱싱 경로/확장자, 핫키, 독 바, 테마 선택
- **플러그인**: 플러그인 설치/제거 (PluginGalleryViewModel)
- **퀵링크**: QuickLinkEntry 목록 관리
- **AI 스니펫**: AiSnippetTemplate 목록 관리
- **감사 로그**: 로그 파일 열기, 보존 기간 설정
SettingsWindow에서 **제거 (ChatWindow 이관 완료)**:
- AX Agent 탭 전체 → ChatWindow 우측 설정 패널
- 모델/LLM 서비스 설정 → 설정 패널 [모델 & 서비스] 섹션
- 에이전트 파라미터(반복, 재시도, 병렬) → 설정 패널 [에이전트 동작] 섹션
- 도구 개별 토글 → 설정 패널 [도구 관리] 섹션 + 헤더 바 아이콘
- MCP 서버 활성화 → 설정 패널 [MCP 서버] 섹션 (등록/삭제는 SettingsWindow)
- 프리셋 관리 → 설정 패널 [프리셋] 섹션
- 프로젝트 규칙, 작업 폴더 → 설정 패널 [고급] 섹션
---
### 입력 영역 (AgentInputArea) — Claude.ai + Codex 스타일
```
┌──────────────────────────────────────────────────────┐
│ @멘션 /스킬 📎 첨부 │
│ ┌──────────────────────────────────────────────────┐ │
│ │ 메시지를 입력하세요… │ │
│ │ │ │
│ └──────────────────────────────────────────────────┘ │
│ [모델명 칩] [권한 칩] [Plan 칩] ⚙설정 [▶전송] │
└──────────────────────────────────────────────────────┘
```
- **상단 툴바**: `@` (파일 멘션 자동완성) · `/` (스킬/슬래시 명령) · 📎 (파일/이미지 첨부)
- **텍스트 입력**: 최소 60px, 최대 220px 자동 확장, Ctrl+Enter 전송
- **하단 칩 열**: 현재 모델명 칩 (클릭 시 변경) + 권한 모드 칩 + Plan 칩 — 항상 현재 상태 표시
- **⚙ 버튼**: 우측 설정 패널 토글 (헤더 바와 동일 동작)
- **전송 버튼 [▶]**: 실행 중 → `[■ 중단]`으로 전환, Border + MouseLeftButtonUp 패턴
---
### 구현 클래스 구조
```
Views/
ChatWindow.xaml # 최상위 창 — 3-pane Grid 구조
ChatWindow.xaml.cs # 탭 전환, 패널 토글, 입력 처리
Controls/ # 재사용 UserControl
AgentSidebarView.xaml # 탭 세그먼트 + 프리셋 + 대화 이력
AgentSidebarView.xaml.cs
AgentSessionHeaderBar.xaml # 모델/Plan/권한/도구 칩 바
AgentSessionHeaderBar.xaml.cs
AgentSettingsPanel.xaml # 우측 슬라이드인 설정 패널 (전체 AX Agent 설정)
AgentSettingsPanel.xaml.cs
AgentInputArea.xaml # 멀티기능 입력 영역
AgentInputArea.xaml.cs
AgentDiffPanel.xaml # 우측 Diff 뷰 패널
AgentDiffPanel.xaml.cs
ViewModels/
ChatWindowViewModel.cs # 창 통합 VM (탭, 패널 상태)
AgentSidebarViewModel.cs # 사이드바 VM (탭/프리셋/이력)
AgentSessionHeaderViewModel.cs # 헤더 칩 VM
AgentSettingsPanelViewModel.cs # 설정 패널 VM (모든 AX Agent 설정)
```
#### 파일 배치 원칙
- 기존 `ChatWindow.xaml` / `ChatWindow.xaml.cs` 를 리팩터링. 새 파일을 신규 생성.
- `AgentSettingsPanel`은 독립 UserControl — `ChatWindow``Grid.Column="2"`로 배치.
- 우측 패널 3종 (설정/워크플로우/Diff)은 `Visibility` + `TranslateTransform`으로 전환.
---
### 애니메이션 / 전환 원칙
| 동작 | 구현 | 시간 |
|------|------|------|
| 사이드바 접기/펼치기 | Width 260→48px DoubleAnimation EaseInOut | 180ms |
| 우측 패널 슬라이드인 | TranslateTransform X +300→0 EaseOut | 200ms |
| 우측 패널 슬라이드아웃 | TranslateTransform X 0→+300 EaseIn | 150ms |
| 탭 전환 | Opacity 0→1 + TranslateY +8→0 | 120ms |
| 메시지 신규 등장 | Opacity 0→1 + TranslateY +16→0 EaseOut | 120ms |
| 스트리밍 커서 | Opacity 0↔1 RepeatForever | 500ms |
| 도구 블록 펼치기 | Height Auto DoubleAnimation | 150ms |
---
## 14. 관련 문서
| 문서 | 경로 | 설명 |
|------|------|------|
| 개발 문서 | `docs/DEVELOPMENT.md` | 아키텍처, 핸들러, 버전 이력, 개발 원칙 상세 |
| AX Agent 로드맵 | `docs/AGENT_ROADMAP.md` | 대화 서비스 고도화 계획 (Phase별) — HTML은 요청 시만 갱신 |
| 런처 로드맵 | `docs/LAUNCHER_ROADMAP.md` | 런처 고도화 계획 (Phase별) — HTML은 요청 시만 갱신 |
| 차기 종합 계획 | `docs/NEXT_ROADMAP.md` | v1.7.1~v2.0 CC 내부 문서 기반 전면 개정판 — HTML은 요청 시만 갱신 |
| Claude Code 참고 문서 | `docs/claude-code-docs-main/` | CC 훅·스킬·멀티에이전트·권한·메모리·SDK 내부 아키텍처 문서 |
| 사용자 가이드 | `src/AxCopilot/Assets/AX Copilot 사용가이드.htm` | 단축키/예약어 가이드 |
| 브랜딩 가이드 | `src/AxCopilot/Assets/BRANDING_가이드.md` | 아이콘/색상/명칭 규칙 |

221
README.md Normal file
View File

@@ -0,0 +1,221 @@
# AX Commander
Windows 전용 시맨틱 런처 & 워크스페이스 매니저
> Alfred (macOS)에서 영감을 받아 Windows 환경에 최적화된 키보드 중심 생산성 도구입니다.
---
## 주요 기능 (프리픽스 치트시트)
| 프리픽스 | 기능 | 예시 |
|----------|------|------|
| *(없음)* | 앱·파일 퍼지 검색 + 앱 단축키 | `vscode`, `크롬`, `ㅅㄷ` (초성) |
| `=` | **계산기** — 수식 즉시 계산, Enter로 결과 복사 | `= sqrt(16)`, `= 2^10`, `= sin(30)` |
| `/` | **시스템 명령** — 잠금·절전·재시작·종료 | `/lock`, `/sleep`, `/shutdown` |
| `;` | **텍스트 스니펫** — 키워드로 저장된 텍스트 붙여넣기 | `;addr`, `;sig` |
| `#` | **클립보드 히스토리** — 복사 이력 검색 & 재사용 | `# hello`, `#` |
| `@` | URL 단축키 | `@gh` → GitHub 열기 |
| `~` | 폴더 단축키 | `~dl` → Downloads 폴더 |
| `>` | 명령 실행 / 배치 단축키 | `>git status` |
| `$` | 클립보드 텍스트 변환 (12종) | `$json`, `$b64e`, `$upper`, `$ts` |
| `!` | 워크스페이스 저장·복원 | `!save work`, `!restore work` |
### Alfred 대비 기능 현황
| 기능 | Alfred | AX Commander |
|------|--------|-----------------|
| 앱 퍼지 검색 | ✅ | ✅ |
| 한국어 초성 검색 | ❌ | ✅ |
| 파일 검색 | ✅ | ✅ (exe/lnk/bat/ps1) |
| 계산기 | ✅ | ✅ `=` 프리픽스 |
| 시스템 명령 | ✅ | ✅ `/` 프리픽스 (7가지) |
| 텍스트 스니펫 | ✅ | ✅ `;` 프리픽스 + 변수 치환 |
| 클립보드 히스토리 | ✅ | ✅ `#` 프리픽스 |
| 클립보드 변환 | ❌ | ✅ `$` 프리픽스 (12종) |
| URL 단축키 | ✅ | ✅ `@` 프리픽스 |
| 폴더 단축키 | ✅ | ✅ `~` 프리픽스 |
| 터미널 명령 | ✅ | ✅ `>` 프리픽스 |
| 워크스페이스 관리 | ❌ | ✅ `!` 프리픽스 |
| 플러그인 SDK | ✅ | ✅ (`IActionHandler`) |
| HTTP API 스킬 | ✅ | ✅ (`.skill.json`) |
| 테마 커스터마이징 | ❌ | ✅ (7종 프리셋 + 완전 커스텀) |
| 설정 UI | ✅ | ✅ (7탭 전체 GUI 설정) |
---
## 설치 및 빌드
### 요구 사항
- **OS**: Windows 10 20H2 이상 (Windows 11 권장)
- **.NET**: 8.0 Runtime (Self-contained 빌드 시 불필요)
### 빌드
```bash
# 소스 클론
git clone https://github.com/your/oledic.git
cd "oledic/AX Commander"
# 개발 실행
dotnet run --project src/AxCommander
# Release 단일 파일 빌드 (self-contained)
dotnet publish src/AxCommander -c Release -r win-x64 --self-contained
```
### 데이터 경로
| 용도 | 경로 |
|------|------|
| 설정 파일 | `%APPDATA%\AxCommander\settings.json` |
| 로그 | `%APPDATA%\AxCommander\logs\app-YYYY-MM-DD.log` |
| 스킬 파일 | `%APPDATA%\AxCommander\skills\*.skill.json` |
| 크래시 덤프 | `%APPDATA%\AxCommander\crashes\` |
---
## 사용법
### 키보드 단축키
| 동작 | 단축키 |
|------|--------|
| 런처 열기 / 닫기 | `Alt+Space` |
| 설정 열기 | `Ctrl+,` |
| 다음 항목 선택 | `↓` |
| 이전 항목 선택 | `↑` |
| 실행 | `Enter` |
| 자동완성 (선택 항목 제목 채우기) | `Tab` |
| 닫기 | `Esc` |
### 계산기 예시
```
= 1 + 2 * 3 → 7
= sqrt(144) → 12
= 2^10 → 1024
= sin(30) → 0.5 (도 단위)
= log(1000) → 3
= pi * 5^2 → 78.5398...
= 0xFF + 16 → 271
= round(3.14159, 2) → 3.14
```
### 스니펫 변수
스니펫 내용에서 아래 변수를 사용할 수 있습니다:
| 변수 | 치환 값 | 예시 |
|------|---------|------|
| `{date}` | 오늘 날짜 | `2026-03-23` |
| `{time}` | 현재 시각 | `14:30:00` |
| `{datetime}` | 날짜+시각 | `2026-03-23 14:30:00` |
| `{year}` | 연도 | `2026` |
| `{month}` | 월 (2자리) | `03` |
| `{day}` | 일 (2자리) | `23` |
---
## 플러그인 개발
### DLL 플러그인
`AxCommander.SDK`를 참조하여 `IActionHandler` 인터페이스를 구현합니다.
```csharp
using AxCommander.SDK;
public class MyHandler : IActionHandler
{
public string? Prefix => "?"; // 트리거 프리픽스 (null이면 prefix 없는 핸들러)
public PluginMetadata Metadata => new("MyPlugin", "설명", Version: "1.0");
public async Task<IEnumerable<LauncherItem>> GetItemsAsync(string query, CancellationToken ct)
{
return [ new LauncherItem("제목", "부제목", null, myData, Symbol: "\uE721") ];
}
public Task ExecuteAsync(LauncherItem item, CancellationToken ct)
{
// item.Data로 전달한 데이터를 꺼내 실행 로직 작성
return Task.CompletedTask;
}
}
```
빌드한 DLL 경로를 `settings.json``plugins` 배열에 추가:
```json
{
"plugins": [
{ "path": "C:\\MyPlugins\\MyPlugin.dll", "enabled": true }
]
}
```
### JSON 스킬 (코드 없이 HTTP API 연동)
`%APPDATA%\AxCommander\skills\``.skill.json` 파일을 놓으면 됩니다.
```json
{
"id": "github-search",
"name": "GitHub 검색",
"prefix": "@gh",
"credential": { "type": "bearer_token", "credentialKey": "github_token" },
"request": {
"method": "GET",
"url": "https://api.github.com/search/repositories?q={{INPUT}}&per_page=10"
},
"response": {
"resultsPath": "items",
"titleField": "full_name",
"subtitleField": "description",
"actionUrl": "html_url"
},
"cache": 30
}
```
---
## 테마
7가지 내장 테마와 완전 커스텀 테마를 지원합니다.
| 테마 | 특징 |
|------|------|
| `system` | Windows 다크/라이트 모드 자동 감지 |
| `dark` | 딥 네이비 다크 (기본) |
| `light` | 클린 화이트 라이트 |
| `oled` | 순수 블랙 (OLED 절전) |
| `nord` | Arctic 컬러 팔레트 |
| `monokai` | Sublime Text 스타일 |
| `catppuccin` | Mocha 따뜻한 파스텔 |
| `sepia` | 황갈색 아날로그 감성 |
| `custom` | 14개 색상 완전 커스터마이징 |
설정 UI(`Ctrl+,` → 테마 탭)에서 클릭 한 번으로 즉시 미리보기 적용.
---
## 변경 이력
### v1.6.1 — 전체 점검 수정
| 분류 | 내용 |
|------|------|
| 빌드 오류 수정 | `using System.IO` 누락, `Key.Enter`/`Key.Return` 중복 switch 케이스, `EnumDisplayMonitors` 람다 타입 혼합, `icon.ico` 다이아몬드 픽셀 보석 아이콘으로 교체, nullable 역참조 경고 |
| 런타임 버그 수정 | JSON 스킬 경로 파싱(`field[0` 형식 오류 방지), 클립보드 서비스 스레드 안전성, 디바운스 타이머 원자적 교체, 설정 백업 실패 로깅 |
| 보안 수정 | JSON 스킬 `ActionUrl` 실행 전 `http`/`https` 스킴 검증 추가 |
| 데드 코드 제거 | `SystemCommandHandler` no-op `timer`/`alarm` 항목 제거 |
자세한 내용은 [docs/DEVELOPMENT.md](docs/DEVELOPMENT.md)의 "v1.6 3차 전체 점검 수정" 섹션을 참고하세요.
---
## 라이선스
MIT License

90
build.bat Normal file
View File

@@ -0,0 +1,90 @@
@echo off
chcp 65001 >nul
echo.
echo ========================================
echo AX Copilot - Build Script
echo ========================================
echo.
set APP=src\AxCopilot\AxCopilot.csproj
set ENCRYPTOR=src\AxKeyEncryptor\AxKeyEncryptor.csproj
set OFFLINE=src\AxCopilot.Installer\AxCopilot.Installer.csproj
set OUT=dist
:: Kill running app
tasklist /FI "IMAGENAME eq AxCopilot.exe" 2>nul | find /i "AxCopilot.exe" >nul
if %ERRORLEVEL%==0 (
echo [0] Stopping AxCopilot...
taskkill /IM AxCopilot.exe /F >nul 2>nul
timeout /t 2 /nobreak >nul
)
:: Kill legacy process
tasklist /FI "IMAGENAME eq AxCommander.exe" 2>nul | find /i "AxCommander.exe" >nul
if %ERRORLEVEL%==0 (
echo [0] Stopping legacy AxCommander...
taskkill /IM AxCommander.exe /F >nul 2>nul
timeout /t 2 /nobreak >nul
)
if exist "%OUT%" rd /s /q "%OUT%" 2>nul
mkdir "%OUT%"
mkdir "%OUT%\AxCopilot"
:: ========================================
:: 1. Main app (self-contained, folder)
:: ========================================
echo [1/4] Building main app (self-contained)...
dotnet publish "%APP%" -c Release -o "%OUT%\AxCopilot" --self-contained true --nologo -v quiet
if %ERRORLEVEL% NEQ 0 ( echo [FAILED] Main app build & pause & exit /b 1 )
echo OK - dist\AxCopilot\
echo.
:: ========================================
:: 2. AxKeyEncryptor (developer tool)
:: ========================================
echo [2/4] Building AxKeyEncryptor (WinForms)...
mkdir "%OUT%\AxKeyEncryptor" 2>nul
dotnet publish "%ENCRYPTOR%" -c Release -o "%OUT%\AxKeyEncryptor" --self-contained false --nologo -v quiet
if %ERRORLEVEL% NEQ 0 ( echo [FAILED] AxKeyEncryptor build & pause & exit /b 1 )
del /q "%OUT%\AxKeyEncryptor\*.pdb" 2>nul
echo OK - dist\AxKeyEncryptor\
echo.
:: ========================================
:: 3. Create payload ZIP for installer
:: ========================================
echo [3/4] Creating installer payload ZIP...
powershell -NoProfile -Command "Compress-Archive -Path '%OUT%\AxCopilot\*' -DestinationPath 'src\AxCopilot.Installer\payload.zip' -Force"
echo OK - payload.zip
echo.
:: ========================================
:: 4. Build installer (.NET Framework 4.8)
:: ========================================
echo [4/4] Building installer (.NET Framework 4.8)...
dotnet build "%OFFLINE%" -c Release --nologo -v quiet
if %ERRORLEVEL% NEQ 0 ( echo [FAILED] Installer build & pause & exit /b 1 )
copy /Y "src\AxCopilot.Installer\bin\Release\net48\AxCopilot_Setup.exe" "%OUT%\" >nul
for %%F in ("%OUT%\AxCopilot_Setup.exe") do echo OK - AxCopilot_Setup.exe (%%~zF bytes)
echo.
:: ========================================
:: Cleanup
:: ========================================
:: Remove debug symbols and metadata (anti-decompile)
del /q "%OUT%\*.pdb" 2>nul
del /q "%OUT%\AxCopilot\*.pdb" 2>nul
del /q "%OUT%\AxCopilot\*.xml" 2>nul
del /q "%OUT%\*.deps.json" 2>nul
del /q "%OUT%\*.runtimeconfig.json" 2>nul
del /q "src\AxCopilot.Installer\payload.zip" 2>nul
echo ========================================
echo Build Complete!
echo ========================================
echo.
echo dist\AxCopilot\ Main app (EXE + DLL)
echo dist\AxKeyEncryptor\ Settings Encryptor (dev tool)
echo dist\AxCopilot_Setup.exe Installer (offline, .NET 4.8)
echo.
pause

14
build_output.txt Normal file
View File

@@ -0,0 +1,14 @@
복원할 프로젝트를 확인하는 중...
E:\AX Copilot\src\AxCopilot\AxCopilot.csproj을(를) 1.41초 동안 복원했습니다.
E:\AX Copilot\src\AxCopilot.Tests\AxCopilot.Tests.csproj을(를) 1.41초 동안 복원했습니다.
복원할 4 프로젝트의 2이(가) 최신 상태입니다.
AxCopilot.SDK -> E:\AX Copilot\src\AxCopilot.SDK\bin\Debug\net8.0-windows\AxCopilot.SDK.dll
AxCopilot -> E:\AX Copilot\src\AxCopilot\bin\Debug\net8.0-windows\win-x64\AxCopilot.dll
AxKeyEncryptor -> E:\AX Copilot\src\AxKeyEncryptor\bin\Debug\net8.0-windows\AxKeyEncryptor.dll
AxCopilot.Tests -> E:\AX Copilot\src\AxCopilot.Tests\bin\Debug\net8.0-windows\AxCopilot.Tests.dll
빌드했습니다.
경고 0개
오류 0개
경과 시간: 00:00:07.34

BIN
dist/AxCopilot/Accessibility.dll vendored Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 519 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 615 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 553 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 247 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 555 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 488 B

BIN
dist/AxCopilot/Assets/guide_dev.enc vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/Assets/guide_user.enc vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/Assets/icon.ico vendored Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

BIN
dist/AxCopilot/AxCopilot.SDK.dll vendored Normal file

Binary file not shown.

1319
dist/AxCopilot/AxCopilot.deps.json vendored Normal file

File diff suppressed because it is too large Load Diff

BIN
dist/AxCopilot/AxCopilot.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/AxCopilot.exe vendored Normal file

Binary file not shown.

View File

@@ -0,0 +1,19 @@
{
"runtimeOptions": {
"tfm": "net8.0",
"includedFrameworks": [
{
"name": "Microsoft.NETCore.App",
"version": "8.0.25"
},
{
"name": "Microsoft.WindowsDesktop.App",
"version": "8.0.25"
}
],
"configProperties": {
"System.Reflection.Metadata.MetadataUpdater.IsSupported": false,
"CSWINRT_USE_WINDOWS_UI_XAML_PROJECTIONS": false
}
}
}

BIN
dist/AxCopilot/D3DCompiler_47_cor3.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/DirectWriteForwarder.dll vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/Markdig.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/Microsoft.CSharp.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/Microsoft.Data.Sqlite.dll vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/Microsoft.VisualBasic.dll vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/PenImc_cor3.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/PresentationCore.dll vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/PresentationFramework.dll vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/PresentationUI.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/ReachFramework.dll vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/SQLitePCLRaw.core.dll vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/System.AppContext.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/System.Buffers.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/System.CodeDom.dll vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/System.Collections.dll vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/System.ComponentModel.dll vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/System.Configuration.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/System.Console.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/System.Core.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/System.Data.Common.dll vendored Normal file

Binary file not shown.

Binary file not shown.

BIN
dist/AxCopilot/System.Data.dll vendored Normal file

Binary file not shown.

BIN
dist/AxCopilot/System.Design.dll vendored Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Some files were not shown because too many files have changed in this diff Show More