v2.2.268-291: /email 통합(브리핑·스캔·워처) + stocks 자동편입·매수알림 + 지식문서·메신저 중앙화
[/email — Gmail + 클래식 Outlook (신규 기능군)]
- 기본 명령: brief(PM 업무 허브 대시보드)·list·read·wikify·reply(Drafts만)·open(Outlook 창 열기)
- Gmail: 자체 OAuth gmail.readonly 스코프 추가 (재연결 1회 필요) / Outlook: COM(PowerShell), 전 계정(Store)·하위 폴더(120개 가드) 수집, To/CC 판별
- 브리핑: 상태 3분류(결정/회신/확인만)·P등급 영향·프로젝트 위험도·Today Focus·오늘 완료 예상·기간 명시 시 표시 범위 제한
- 영속 상태(email_state.json): 회신대기/공넘김 상태머신, 창 밖 미회신 보존, done old N 일괄정리
- 워처: 주기 자동 수집 + 미회신 임계 알림(기본 끔) + 데일리 브리핑 '답할 이메일' 섹션
- 전체 스캔(/email scan): 자동 위키화(회차 15건·레지스트리·파일삭제 감지 재생성)·진행 표시(streamProgress)
- 자동발신 필터 확장(Confluence·AWS·광고·[Notification]) + 라벨 오타 후처리(polishBriefLabels)
[/stocks]
- discover 자동 편입(stocks.json) + 매수권장가 224일선 자동 산출
- 매수사정권 진입 전이 감지 → 텔레그램 1회 알림 (직전신호 추적)
- 버그 수정: '미충족'이 .includes('충족')에 오탐되던 필터 판정
[지식 문서 파이프라인 통일 (wikiFormat/wikiSave)]
- PC 로컬 직접 저장 (NAS 볼륨 미아 문제 해소) — 설정 폴더 → 두뇌 → 워크스페이스
- 이메일 전용 Email_Wiki 분리(email.wikifySavePath) + 봉투(frontmatter) 자동 통일
- 정본 프롬프트 단일 빌더(buildKnowledgeWikiPrompt) — web/email 래퍼화, 템플릿 fetch 일원화
- 이메일 문서 시점 3중 방어: 기준시점 헤더·제목=메일 최신일·날짜 없는 현재형 금지
- docs/WIKI_DOC_PIPELINE.md 개발 규약
[텔레그램 메신저 중앙화 (messageFormat)]
- 단일 렌더러 renderTelegram — 5개 발신 빌더(주식보고·매수시그널·Top5·데일리·미회신) 통일
- 주식 보고 시각 하드코딩 제거(messenger.stocksReportTimes CSV) + 알림 토글 2종
- 설정 패널 '메신저' 탭 신설 · '이메일' 토글 섹션(Gmail/Outlook on/off)
테스트 803개 통과 (이메일 상태머신·CC판별·포맷·매수전이·봉투 등 60+ 신규)
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -103,6 +103,7 @@ interface SettingsState {
|
||||
bridgeNasToken: string;
|
||||
/** Empty → results saved to the Bridge's WIKI_RAW_PATH default. */
|
||||
savePath: string;
|
||||
emailWikifySavePath: string;
|
||||
crawlDepth: number;
|
||||
maxPages: number;
|
||||
synthesisTemperature: number;
|
||||
@@ -131,6 +132,18 @@ interface SettingsState {
|
||||
anthropic: { enabled: boolean; hasApiKey: boolean; defaultModel: string };
|
||||
gemini: { enabled: boolean; hasApiKey: boolean; defaultModel: string };
|
||||
};
|
||||
email: {
|
||||
outlookEnabled: boolean;
|
||||
gmailEnabled: boolean;
|
||||
};
|
||||
messenger: {
|
||||
stocksReportTimes: string;
|
||||
briefingEnabled: boolean;
|
||||
briefingTime: string;
|
||||
buySignalAlert: boolean;
|
||||
discoverTopFive: boolean;
|
||||
emailOverdueHours: number;
|
||||
};
|
||||
devilAgent: {
|
||||
enabled: boolean;
|
||||
};
|
||||
@@ -261,6 +274,10 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
||||
case 'advanced.update':
|
||||
await this._handleAdvancedUpdate(msg);
|
||||
return;
|
||||
case 'messenger.update':
|
||||
await this._handleMessengerUpdate(msg);
|
||||
await this._refreshState();
|
||||
break;
|
||||
case 'datacollect.update':
|
||||
await this._handleDatacollectUpdate(msg);
|
||||
return;
|
||||
@@ -616,6 +633,29 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
||||
// /research·/benchmark·/youtube 가 호출하는 Bridge URL 과, 결과물 저장 위치.
|
||||
// savePath 가 비어 있으면 Bridge 의 WIKI_RAW_PATH 환경변수가 저장 위치를 결정한다.
|
||||
|
||||
/** 메신저 탭 (v2.2.284) — 발송 스케줄·알림 토글. */
|
||||
private async _handleMessengerUpdate(msg: any): Promise<void> {
|
||||
if (typeof msg.stocksReportTimes === 'string') {
|
||||
await this._safeConfigUpdate('messenger.stocksReportTimes', msg.stocksReportTimes.trim());
|
||||
}
|
||||
if (typeof msg.briefingEnabled === 'boolean') {
|
||||
await this._safeConfigUpdate('dailyBriefing.enabled', msg.briefingEnabled);
|
||||
}
|
||||
if (typeof msg.briefingTime === 'string' && /^\d{1,2}:\d{2}$/.test(msg.briefingTime.trim())) {
|
||||
await this._safeConfigUpdate('dailyBriefing.time', msg.briefingTime.trim());
|
||||
}
|
||||
if (typeof msg.buySignalAlert === 'boolean') {
|
||||
await this._safeConfigUpdate('messenger.buySignalAlert', msg.buySignalAlert);
|
||||
}
|
||||
if (typeof msg.discoverTopFive === 'boolean') {
|
||||
await this._safeConfigUpdate('messenger.discoverTopFive', msg.discoverTopFive);
|
||||
}
|
||||
if (typeof msg.emailOverdueHours === 'number' && Number.isFinite(msg.emailOverdueHours)) {
|
||||
await this._safeConfigUpdate('email.overdueAlertHours', Math.max(0, Math.floor(msg.emailOverdueHours)));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private async _handleDatacollectUpdate(msg: any): Promise<void> {
|
||||
if (typeof msg.bridgeTarget === 'string') {
|
||||
const t = msg.bridgeTarget.trim() === 'nas' ? 'nas' : 'local';
|
||||
@@ -633,6 +673,15 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
||||
if (typeof msg.savePath === 'string') {
|
||||
await this._safeConfigUpdate('datacollectSavePath', msg.savePath.trim());
|
||||
}
|
||||
if (typeof msg.emailWikifySavePath === 'string') {
|
||||
await this._safeConfigUpdate('email.wikifySavePath', msg.emailWikifySavePath.trim());
|
||||
}
|
||||
if (typeof msg.emailOutlookEnabled === 'boolean') {
|
||||
await this._safeConfigUpdate('email.outlookEnabled', msg.emailOutlookEnabled);
|
||||
}
|
||||
if (typeof msg.emailGmailEnabled === 'boolean') {
|
||||
await this._safeConfigUpdate('email.gmailEnabled', msg.emailGmailEnabled);
|
||||
}
|
||||
if (typeof msg.crawlDepth === 'number' && Number.isFinite(msg.crawlDepth)) {
|
||||
await this._safeConfigUpdate('datacollectCrawlDepth', Math.max(0, Math.min(3, Math.floor(msg.crawlDepth))));
|
||||
}
|
||||
@@ -702,12 +751,25 @@ export class SettingsPanelProvider implements vscode.WebviewViewProvider {
|
||||
bridgeNasUrl: cfg.get<string>('datacollectBridgeNasUrl', '') || '',
|
||||
bridgeNasToken: cfg.get<string>('datacollectBridgeNasToken', '') || '',
|
||||
savePath: cfg.get<string>('datacollectSavePath', '') || '',
|
||||
emailWikifySavePath: cfg.get<string>('email.wikifySavePath', '') || '',
|
||||
crawlDepth: cfg.get<number>('datacollectCrawlDepth', 1) ?? 1,
|
||||
maxPages: cfg.get<number>('datacollectMaxPages', 8) ?? 8,
|
||||
synthesisTemperature: cfg.get<number>('datacollectSynthesisTemperature', 0.1) ?? 0.1,
|
||||
},
|
||||
google: this._buildGoogleState(),
|
||||
providers: await this._buildProvidersState(),
|
||||
email: {
|
||||
outlookEnabled: cfg.get<boolean>('email.outlookEnabled', true) !== false,
|
||||
gmailEnabled: cfg.get<boolean>('email.gmailEnabled', true) !== false,
|
||||
},
|
||||
messenger: {
|
||||
stocksReportTimes: cfg.get<string>('messenger.stocksReportTimes', '09:00,15:00') || '',
|
||||
briefingEnabled: cfg.get<boolean>('dailyBriefing.enabled', true) !== false,
|
||||
briefingTime: cfg.get<string>('dailyBriefing.time', '09:30') || '09:30',
|
||||
buySignalAlert: cfg.get<boolean>('messenger.buySignalAlert', true) !== false,
|
||||
discoverTopFive: cfg.get<boolean>('messenger.discoverTopFive', true) !== false,
|
||||
emailOverdueHours: cfg.get<number>('email.overdueAlertHours', 0) ?? 0,
|
||||
},
|
||||
devilAgent: { enabled: cfg.get<boolean>('devilAgent.enabled', false) },
|
||||
bannerError: this._bannerError,
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user