katashiro 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/src/index.ts ADDED
@@ -0,0 +1,36 @@
1
+ /**
2
+ * @nahisaho/katashiro-vscode-extension
3
+ * VS Code拡張機能
4
+ *
5
+ * @requirement REQ-VSCODE-001 ~ REQ-VSCODE-004
6
+ * @design DES-KATASHIRO-001 §2.3 VS Code Extension
7
+ */
8
+
9
+ // Extension entry points
10
+ export { activate, deactivate } from './extension.js';
11
+
12
+ // Extension controller
13
+ export { KatashiroExtension, type KatashiroConfig } from './katashiro-extension.js';
14
+
15
+ // Commands
16
+ export { CommandManager } from './commands/index.js';
17
+
18
+ // Views
19
+ export {
20
+ ResearchViewProvider,
21
+ ResearchItem,
22
+ KnowledgeViewProvider,
23
+ KnowledgeItem,
24
+ HistoryViewProvider,
25
+ HistoryItem,
26
+ type HistoryEntry,
27
+ type HistoryEntryType,
28
+ } from './views/index.js';
29
+
30
+ // UI
31
+ export {
32
+ OutputChannelManager,
33
+ StatusBarManager,
34
+ type LogLevel,
35
+ type StatusBarState,
36
+ } from './ui/index.js';
@@ -0,0 +1,125 @@
1
+ /**
2
+ * KatashiroExtension - Main extension controller
3
+ *
4
+ * Manages commands, views, and integration with KATASHIRO packages
5
+ *
6
+ * @module katashiro
7
+ * @task TSK-070
8
+ */
9
+
10
+ import * as vscode from 'vscode';
11
+ import { CommandManager } from './commands/command-manager.js';
12
+ import { ResearchViewProvider } from './views/research-view-provider.js';
13
+ import { KnowledgeViewProvider } from './views/knowledge-view-provider.js';
14
+ import { HistoryViewProvider } from './views/history-view-provider.js';
15
+ import { StatusBarManager } from './ui/status-bar-manager.js';
16
+ import { OutputChannelManager } from './ui/output-channel-manager.js';
17
+
18
+ /**
19
+ * Extension configuration
20
+ */
21
+ export interface KatashiroConfig {
22
+ searchEngine: 'duckduckgo' | 'google' | 'bing';
23
+ maxSearchResults: number;
24
+ outputFormat: 'markdown' | 'html' | 'json';
25
+ autoSaveKnowledge: boolean;
26
+ mcpServerPort: number;
27
+ }
28
+
29
+ /**
30
+ * KatashiroExtension
31
+ *
32
+ * Main controller for the KATASHIRO VS Code extension
33
+ */
34
+ export class KatashiroExtension {
35
+ private commandManager: CommandManager;
36
+ private researchViewProvider: ResearchViewProvider;
37
+ private knowledgeViewProvider: KnowledgeViewProvider;
38
+ private historyViewProvider: HistoryViewProvider;
39
+ private statusBarManager: StatusBarManager;
40
+ private outputChannelManager: OutputChannelManager;
41
+
42
+ constructor(private readonly context: vscode.ExtensionContext) {
43
+ // Initialize managers
44
+ this.outputChannelManager = new OutputChannelManager();
45
+ this.statusBarManager = new StatusBarManager();
46
+ this.commandManager = new CommandManager(
47
+ context,
48
+ this.outputChannelManager
49
+ );
50
+
51
+ // Initialize view providers
52
+ this.researchViewProvider = new ResearchViewProvider();
53
+ this.knowledgeViewProvider = new KnowledgeViewProvider();
54
+ this.historyViewProvider = new HistoryViewProvider();
55
+ }
56
+
57
+ /**
58
+ * Activate the extension
59
+ */
60
+ async activate(): Promise<void> {
61
+ // Register commands
62
+ this.commandManager.registerAll();
63
+
64
+ // Register view providers
65
+ this.registerViewProviders();
66
+
67
+ // Initialize status bar
68
+ this.statusBarManager.show();
69
+
70
+ // Log activation
71
+ this.outputChannelManager.log('KATASHIRO extension activated');
72
+ }
73
+
74
+ /**
75
+ * Deactivate the extension
76
+ */
77
+ async deactivate(): Promise<void> {
78
+ this.statusBarManager.dispose();
79
+ this.outputChannelManager.dispose();
80
+ this.outputChannelManager.log('KATASHIRO extension deactivated');
81
+ }
82
+
83
+ /**
84
+ * Get current configuration
85
+ */
86
+ getConfig(): KatashiroConfig {
87
+ const config = vscode.workspace.getConfiguration('katashiro');
88
+ return {
89
+ searchEngine: config.get('searchEngine', 'duckduckgo'),
90
+ maxSearchResults: config.get('maxSearchResults', 10),
91
+ outputFormat: config.get('outputFormat', 'markdown'),
92
+ autoSaveKnowledge: config.get('autoSaveKnowledge', true),
93
+ mcpServerPort: config.get('mcpServerPort', 3000),
94
+ };
95
+ }
96
+
97
+ /**
98
+ * Register view providers
99
+ */
100
+ private registerViewProviders(): void {
101
+ // Research view
102
+ this.context.subscriptions.push(
103
+ vscode.window.registerTreeDataProvider(
104
+ 'katashiro.research',
105
+ this.researchViewProvider
106
+ )
107
+ );
108
+
109
+ // Knowledge view
110
+ this.context.subscriptions.push(
111
+ vscode.window.registerTreeDataProvider(
112
+ 'katashiro.knowledge',
113
+ this.knowledgeViewProvider
114
+ )
115
+ );
116
+
117
+ // History view
118
+ this.context.subscriptions.push(
119
+ vscode.window.registerTreeDataProvider(
120
+ 'katashiro.history',
121
+ this.historyViewProvider
122
+ )
123
+ );
124
+ }
125
+ }
@@ -0,0 +1,9 @@
1
+ /**
2
+ * UI module exports
3
+ */
4
+
5
+ export {
6
+ OutputChannelManager,
7
+ type LogLevel,
8
+ } from './output-channel-manager.js';
9
+ export { StatusBarManager, type StatusBarState } from './status-bar-manager.js';
@@ -0,0 +1,105 @@
1
+ /**
2
+ * OutputChannelManager - VS Code output channel management
3
+ *
4
+ * @module katashiro
5
+ * @task TSK-073
6
+ */
7
+
8
+ import * as vscode from 'vscode';
9
+
10
+ /**
11
+ * Log level
12
+ */
13
+ export type LogLevel = 'info' | 'warn' | 'error' | 'debug';
14
+
15
+ /**
16
+ * OutputChannelManager
17
+ *
18
+ * Manages VS Code output channel for KATASHIRO
19
+ */
20
+ export class OutputChannelManager {
21
+ private channel: vscode.OutputChannel;
22
+ private debugMode: boolean;
23
+
24
+ constructor(name = 'KATASHIRO') {
25
+ this.channel = vscode.window.createOutputChannel(name);
26
+ this.debugMode =
27
+ vscode.workspace.getConfiguration('katashiro').get('debug', false);
28
+ }
29
+
30
+ /**
31
+ * Log a message
32
+ */
33
+ log(message: string, level: LogLevel = 'info'): void {
34
+ const timestamp = new Date().toISOString();
35
+ const prefix = this.getLevelPrefix(level);
36
+ this.channel.appendLine(`[${timestamp}] ${prefix} ${message}`);
37
+ }
38
+
39
+ /**
40
+ * Log info message
41
+ */
42
+ info(message: string): void {
43
+ this.log(message, 'info');
44
+ }
45
+
46
+ /**
47
+ * Log warning message
48
+ */
49
+ warn(message: string): void {
50
+ this.log(message, 'warn');
51
+ }
52
+
53
+ /**
54
+ * Log error message
55
+ */
56
+ error(message: string): void {
57
+ this.log(message, 'error');
58
+ }
59
+
60
+ /**
61
+ * Log debug message (only if debug mode is enabled)
62
+ */
63
+ debug(message: string): void {
64
+ if (this.debugMode) {
65
+ this.log(message, 'debug');
66
+ }
67
+ }
68
+
69
+ /**
70
+ * Show the output channel
71
+ */
72
+ show(): void {
73
+ this.channel.show();
74
+ }
75
+
76
+ /**
77
+ * Clear the output channel
78
+ */
79
+ clear(): void {
80
+ this.channel.clear();
81
+ }
82
+
83
+ /**
84
+ * Dispose the output channel
85
+ */
86
+ dispose(): void {
87
+ this.channel.dispose();
88
+ }
89
+
90
+ /**
91
+ * Get level prefix
92
+ */
93
+ private getLevelPrefix(level: LogLevel): string {
94
+ switch (level) {
95
+ case 'info':
96
+ return 'INFO';
97
+ case 'warn':
98
+ return 'WARN';
99
+ case 'error':
100
+ return 'ERROR';
101
+ case 'debug':
102
+ return 'DEBUG';
103
+ }
104
+ }
105
+ }
@@ -0,0 +1,157 @@
1
+ /**
2
+ * StatusBarManager - VS Code status bar management
3
+ *
4
+ * @module katashiro
5
+ * @task TSK-073
6
+ */
7
+
8
+ import * as vscode from 'vscode';
9
+
10
+ /**
11
+ * Status bar state
12
+ */
13
+ export type StatusBarState = 'idle' | 'working' | 'success' | 'error';
14
+
15
+ /**
16
+ * StatusBarManager
17
+ *
18
+ * Manages VS Code status bar item for KATASHIRO
19
+ */
20
+ export class StatusBarManager {
21
+ private statusBarItem: vscode.StatusBarItem;
22
+ private currentState: StatusBarState = 'idle';
23
+ private spinnerInterval: NodeJS.Timeout | null = null;
24
+ private spinnerFrames = ['$(sync~spin)', '$(loading~spin)'];
25
+ private spinnerIndex = 0;
26
+
27
+ constructor() {
28
+ this.statusBarItem = vscode.window.createStatusBarItem(
29
+ vscode.StatusBarAlignment.Right,
30
+ 100
31
+ );
32
+ this.statusBarItem.command = 'katashiro.webSearch';
33
+ this.setIdle();
34
+ }
35
+
36
+ /**
37
+ * Show the status bar item
38
+ */
39
+ show(): void {
40
+ this.statusBarItem.show();
41
+ }
42
+
43
+ /**
44
+ * Hide the status bar item
45
+ */
46
+ hide(): void {
47
+ this.statusBarItem.hide();
48
+ }
49
+
50
+ /**
51
+ * Dispose the status bar item
52
+ */
53
+ dispose(): void {
54
+ this.stopSpinner();
55
+ this.statusBarItem.dispose();
56
+ }
57
+
58
+ /**
59
+ * Get current state
60
+ */
61
+ getState(): StatusBarState {
62
+ return this.currentState;
63
+ }
64
+
65
+ /**
66
+ * Set idle state
67
+ */
68
+ setIdle(message = 'KATASHIRO'): void {
69
+ this.stopSpinner();
70
+ this.currentState = 'idle';
71
+ this.statusBarItem.text = `$(search) ${message}`;
72
+ this.statusBarItem.tooltip = 'Click to search';
73
+ this.statusBarItem.backgroundColor = undefined;
74
+ }
75
+
76
+ /**
77
+ * Set working state
78
+ */
79
+ setWorking(message = 'Working...'): void {
80
+ this.currentState = 'working';
81
+ this.statusBarItem.tooltip = message;
82
+ this.startSpinner(message);
83
+ }
84
+
85
+ /**
86
+ * Set success state
87
+ */
88
+ setSuccess(message = 'Done', autoResetMs = 3000): void {
89
+ this.stopSpinner();
90
+ this.currentState = 'success';
91
+ this.statusBarItem.text = `$(check) ${message}`;
92
+ this.statusBarItem.tooltip = message;
93
+ this.statusBarItem.backgroundColor = new vscode.ThemeColor(
94
+ 'statusBarItem.prominentBackground'
95
+ );
96
+
97
+ if (autoResetMs > 0) {
98
+ setTimeout(() => {
99
+ if (this.currentState === 'success') {
100
+ this.setIdle();
101
+ }
102
+ }, autoResetMs);
103
+ }
104
+ }
105
+
106
+ /**
107
+ * Set error state
108
+ */
109
+ setError(message = 'Error', autoResetMs = 5000): void {
110
+ this.stopSpinner();
111
+ this.currentState = 'error';
112
+ this.statusBarItem.text = `$(error) ${message}`;
113
+ this.statusBarItem.tooltip = message;
114
+ this.statusBarItem.backgroundColor = new vscode.ThemeColor(
115
+ 'statusBarItem.errorBackground'
116
+ );
117
+
118
+ if (autoResetMs > 0) {
119
+ setTimeout(() => {
120
+ if (this.currentState === 'error') {
121
+ this.setIdle();
122
+ }
123
+ }, autoResetMs);
124
+ }
125
+ }
126
+
127
+ /**
128
+ * Start spinner animation
129
+ */
130
+ private startSpinner(message: string): void {
131
+ this.stopSpinner();
132
+ this.updateSpinner(message);
133
+ this.spinnerInterval = setInterval(() => {
134
+ this.updateSpinner(message);
135
+ }, 500);
136
+ }
137
+
138
+ /**
139
+ * Update spinner frame
140
+ */
141
+ private updateSpinner(message: string): void {
142
+ const frame = this.spinnerFrames[this.spinnerIndex];
143
+ this.statusBarItem.text = `${frame} ${message}`;
144
+ this.spinnerIndex = (this.spinnerIndex + 1) % this.spinnerFrames.length;
145
+ }
146
+
147
+ /**
148
+ * Stop spinner animation
149
+ */
150
+ private stopSpinner(): void {
151
+ if (this.spinnerInterval) {
152
+ clearInterval(this.spinnerInterval);
153
+ this.spinnerInterval = null;
154
+ }
155
+ this.spinnerIndex = 0;
156
+ }
157
+ }
@@ -0,0 +1,221 @@
1
+ /**
2
+ * HistoryViewProvider - History panel tree view
3
+ *
4
+ * @module katashiro
5
+ * @task TSK-072
6
+ */
7
+
8
+ import * as vscode from 'vscode';
9
+
10
+ /**
11
+ * History entry type
12
+ */
13
+ export type HistoryEntryType =
14
+ | 'search'
15
+ | 'analysis'
16
+ | 'summary'
17
+ | 'research'
18
+ | 'report';
19
+
20
+ /**
21
+ * History entry
22
+ */
23
+ export interface HistoryEntry {
24
+ id: string;
25
+ type: HistoryEntryType;
26
+ title: string;
27
+ timestamp: Date;
28
+ details?: string;
29
+ }
30
+
31
+ /**
32
+ * History item for tree view
33
+ */
34
+ export class HistoryItem extends vscode.TreeItem {
35
+ constructor(
36
+ public readonly entry: HistoryEntry,
37
+ public readonly collapsibleState: vscode.TreeItemCollapsibleState
38
+ ) {
39
+ super(entry.title, collapsibleState);
40
+ this.description = this.formatTime(entry.timestamp);
41
+ this.tooltip = `${entry.type}: ${entry.title}\n${entry.details || ''}`;
42
+ this.contextValue = entry.type;
43
+
44
+ // Set icon based on type
45
+ switch (entry.type) {
46
+ case 'search':
47
+ this.iconPath = new vscode.ThemeIcon('search');
48
+ break;
49
+ case 'analysis':
50
+ this.iconPath = new vscode.ThemeIcon('pulse');
51
+ break;
52
+ case 'summary':
53
+ this.iconPath = new vscode.ThemeIcon('note');
54
+ break;
55
+ case 'research':
56
+ this.iconPath = new vscode.ThemeIcon('book');
57
+ break;
58
+ case 'report':
59
+ this.iconPath = new vscode.ThemeIcon('file-text');
60
+ break;
61
+ }
62
+ }
63
+
64
+ /**
65
+ * Format timestamp
66
+ */
67
+ private formatTime(date: Date): string {
68
+ const now = new Date();
69
+ const diffMs = now.getTime() - date.getTime();
70
+ const diffMins = Math.floor(diffMs / 60000);
71
+
72
+ if (diffMins < 1) return 'just now';
73
+ if (diffMins < 60) return `${diffMins}m ago`;
74
+
75
+ const diffHours = Math.floor(diffMins / 60);
76
+ if (diffHours < 24) return `${diffHours}h ago`;
77
+
78
+ const diffDays = Math.floor(diffHours / 24);
79
+ if (diffDays < 7) return `${diffDays}d ago`;
80
+
81
+ return date.toLocaleDateString();
82
+ }
83
+ }
84
+
85
+ /**
86
+ * HistoryViewProvider
87
+ *
88
+ * Provides tree data for the History panel
89
+ */
90
+ export class HistoryViewProvider
91
+ implements vscode.TreeDataProvider<HistoryItem>
92
+ {
93
+ private _onDidChangeTreeData = new vscode.EventEmitter<
94
+ HistoryItem | undefined | null | void
95
+ >();
96
+ readonly onDidChangeTreeData = this._onDidChangeTreeData.event;
97
+
98
+ private history: HistoryEntry[] = [];
99
+ private maxEntries = 50;
100
+
101
+ /**
102
+ * Refresh the tree view
103
+ */
104
+ refresh(): void {
105
+ this._onDidChangeTreeData.fire();
106
+ }
107
+
108
+ /**
109
+ * Add a history entry
110
+ */
111
+ addEntry(
112
+ type: HistoryEntryType,
113
+ title: string,
114
+ details?: string
115
+ ): HistoryEntry {
116
+ const entry: HistoryEntry = {
117
+ id: this.generateId(),
118
+ type,
119
+ title,
120
+ timestamp: new Date(),
121
+ details,
122
+ };
123
+
124
+ this.history.unshift(entry);
125
+
126
+ // Limit history size
127
+ if (this.history.length > this.maxEntries) {
128
+ this.history = this.history.slice(0, this.maxEntries);
129
+ }
130
+
131
+ this.refresh();
132
+ return entry;
133
+ }
134
+
135
+ /**
136
+ * Clear all history
137
+ */
138
+ clear(): void {
139
+ this.history = [];
140
+ this.refresh();
141
+ }
142
+
143
+ /**
144
+ * Get entry by ID
145
+ */
146
+ getEntry(id: string): HistoryEntry | undefined {
147
+ return this.history.find((e) => e.id === id);
148
+ }
149
+
150
+ /**
151
+ * Get all entries
152
+ */
153
+ getAll(): HistoryEntry[] {
154
+ return [...this.history];
155
+ }
156
+
157
+ /**
158
+ * Get tree item
159
+ */
160
+ getTreeItem(element: HistoryItem): vscode.TreeItem {
161
+ return element;
162
+ }
163
+
164
+ /**
165
+ * Get children
166
+ */
167
+ getChildren(element?: HistoryItem): Thenable<HistoryItem[]> {
168
+ if (element) {
169
+ // No children for history items
170
+ return Promise.resolve([]);
171
+ }
172
+
173
+ // Root level - all history entries
174
+ if (this.history.length === 0) {
175
+ return Promise.resolve([]);
176
+ }
177
+
178
+ // Group by date
179
+ const today: HistoryEntry[] = [];
180
+ const yesterday: HistoryEntry[] = [];
181
+ const thisWeek: HistoryEntry[] = [];
182
+ const older: HistoryEntry[] = [];
183
+
184
+ const now = new Date();
185
+ const todayStart = new Date(
186
+ now.getFullYear(),
187
+ now.getMonth(),
188
+ now.getDate()
189
+ );
190
+ const yesterdayStart = new Date(todayStart.getTime() - 86400000);
191
+ const weekStart = new Date(todayStart.getTime() - 7 * 86400000);
192
+
193
+ for (const entry of this.history) {
194
+ const time = entry.timestamp.getTime();
195
+ if (time >= todayStart.getTime()) {
196
+ today.push(entry);
197
+ } else if (time >= yesterdayStart.getTime()) {
198
+ yesterday.push(entry);
199
+ } else if (time >= weekStart.getTime()) {
200
+ thisWeek.push(entry);
201
+ } else {
202
+ older.push(entry);
203
+ }
204
+ }
205
+
206
+ // Return all entries as flat list
207
+ return Promise.resolve(
208
+ this.history.map(
209
+ (entry) =>
210
+ new HistoryItem(entry, vscode.TreeItemCollapsibleState.None)
211
+ )
212
+ );
213
+ }
214
+
215
+ /**
216
+ * Generate unique ID
217
+ */
218
+ private generateId(): string {
219
+ return `history-${Date.now()}-${Math.random().toString(36).substring(2, 9)}`;
220
+ }
221
+ }
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Views module exports
3
+ */
4
+
5
+ export { ResearchViewProvider, ResearchItem } from './research-view-provider.js';
6
+ export {
7
+ KnowledgeViewProvider,
8
+ KnowledgeItem,
9
+ } from './knowledge-view-provider.js';
10
+ export {
11
+ HistoryViewProvider,
12
+ HistoryItem,
13
+ type HistoryEntry,
14
+ type HistoryEntryType,
15
+ } from './history-view-provider.js';