oh-my-claude-sisyphus 3.6.3 → 3.7.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +40 -1
- package/commands/hud.md +37 -5
- package/commands/omc-setup.md +105 -0
- package/dist/__tests__/delegation-enforcement-levels.test.d.ts +9 -0
- package/dist/__tests__/delegation-enforcement-levels.test.d.ts.map +1 -0
- package/dist/__tests__/delegation-enforcement-levels.test.js +550 -0
- package/dist/__tests__/delegation-enforcement-levels.test.js.map +1 -0
- package/dist/__tests__/hud/analytics-display.test.js +137 -1
- package/dist/__tests__/hud/analytics-display.test.js.map +1 -1
- package/dist/__tests__/hud-windows.test.d.ts +2 -0
- package/dist/__tests__/hud-windows.test.d.ts.map +1 -0
- package/dist/__tests__/hud-windows.test.js +91 -0
- package/dist/__tests__/hud-windows.test.js.map +1 -0
- package/dist/__tests__/installer.test.js +1 -1
- package/dist/__tests__/rate-limit-wait/daemon.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.js +313 -0
- package/dist/__tests__/rate-limit-wait/daemon.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/integration.test.d.ts +8 -0
- package/dist/__tests__/rate-limit-wait/integration.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/integration.test.js +329 -0
- package/dist/__tests__/rate-limit-wait/integration.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js +167 -0
- package/dist/__tests__/rate-limit-wait/rate-limit-monitor.test.js.map +1 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts +5 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.d.ts.map +1 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.js +295 -0
- package/dist/__tests__/rate-limit-wait/tmux-detector.test.js.map +1 -0
- package/dist/cli/commands/wait.d.ts +52 -0
- package/dist/cli/commands/wait.d.ts.map +1 -0
- package/dist/cli/commands/wait.js +229 -0
- package/dist/cli/commands/wait.js.map +1 -0
- package/dist/cli/index.js +54 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/features/rate-limit-wait/daemon.d.ts +52 -0
- package/dist/features/rate-limit-wait/daemon.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/daemon.js +585 -0
- package/dist/features/rate-limit-wait/daemon.js.map +1 -0
- package/dist/features/rate-limit-wait/index.d.ts +16 -0
- package/dist/features/rate-limit-wait/index.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/index.js +18 -0
- package/dist/features/rate-limit-wait/index.js.map +1 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts +22 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.js +99 -0
- package/dist/features/rate-limit-wait/rate-limit-monitor.js.map +1 -0
- package/dist/features/rate-limit-wait/tmux-detector.d.ts +59 -0
- package/dist/features/rate-limit-wait/tmux-detector.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/tmux-detector.js +304 -0
- package/dist/features/rate-limit-wait/tmux-detector.js.map +1 -0
- package/dist/features/rate-limit-wait/types.d.ts +121 -0
- package/dist/features/rate-limit-wait/types.d.ts.map +1 -0
- package/dist/features/rate-limit-wait/types.js +8 -0
- package/dist/features/rate-limit-wait/types.js.map +1 -0
- package/dist/features/state-manager/index.d.ts.map +1 -1
- package/dist/features/state-manager/index.js +4 -1
- package/dist/features/state-manager/index.js.map +1 -1
- package/dist/hooks/bridge.d.ts +1 -1
- package/dist/hooks/bridge.d.ts.map +1 -1
- package/dist/hooks/bridge.js +50 -4
- package/dist/hooks/bridge.js.map +1 -1
- package/dist/hooks/index.d.ts +5 -0
- package/dist/hooks/index.d.ts.map +1 -1
- package/dist/hooks/index.js +15 -0
- package/dist/hooks/index.js.map +1 -1
- package/dist/hooks/omc-orchestrator/audit.d.ts +2 -1
- package/dist/hooks/omc-orchestrator/audit.d.ts.map +1 -1
- package/dist/hooks/omc-orchestrator/audit.js.map +1 -1
- package/dist/hooks/omc-orchestrator/index.d.ts +7 -0
- package/dist/hooks/omc-orchestrator/index.d.ts.map +1 -1
- package/dist/hooks/omc-orchestrator/index.js +95 -8
- package/dist/hooks/omc-orchestrator/index.js.map +1 -1
- package/dist/hooks/permission-handler/__tests__/index.test.d.ts +2 -0
- package/dist/hooks/permission-handler/__tests__/index.test.d.ts.map +1 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js +291 -0
- package/dist/hooks/permission-handler/__tests__/index.test.js.map +1 -0
- package/dist/hooks/permission-handler/index.d.ts +42 -0
- package/dist/hooks/permission-handler/index.d.ts.map +1 -0
- package/dist/hooks/permission-handler/index.js +107 -0
- package/dist/hooks/permission-handler/index.js.map +1 -0
- package/dist/hooks/plugin-patterns/index.d.ts +5 -0
- package/dist/hooks/plugin-patterns/index.d.ts.map +1 -1
- package/dist/hooks/plugin-patterns/index.js +26 -1
- package/dist/hooks/plugin-patterns/index.js.map +1 -1
- package/dist/hooks/pre-compact/index.d.ts +82 -0
- package/dist/hooks/pre-compact/index.d.ts.map +1 -0
- package/dist/hooks/pre-compact/index.js +265 -0
- package/dist/hooks/pre-compact/index.js.map +1 -0
- package/dist/hooks/session-end/index.d.ts +42 -0
- package/dist/hooks/session-end/index.d.ts.map +1 -0
- package/dist/hooks/session-end/index.js +200 -0
- package/dist/hooks/session-end/index.js.map +1 -0
- package/dist/hooks/setup/index.d.ts +66 -0
- package/dist/hooks/setup/index.d.ts.map +1 -0
- package/dist/hooks/setup/index.js +299 -0
- package/dist/hooks/setup/index.js.map +1 -0
- package/dist/hooks/setup/types.d.ts +25 -0
- package/dist/hooks/setup/types.d.ts.map +1 -0
- package/dist/hooks/setup/types.js +5 -0
- package/dist/hooks/setup/types.js.map +1 -0
- package/dist/hooks/subagent-tracker/index.d.ts +68 -29
- package/dist/hooks/subagent-tracker/index.d.ts.map +1 -1
- package/dist/hooks/subagent-tracker/index.js +316 -131
- package/dist/hooks/subagent-tracker/index.js.map +1 -1
- package/dist/hud/analytics-display.d.ts +16 -0
- package/dist/hud/analytics-display.d.ts.map +1 -1
- package/dist/hud/analytics-display.js +35 -9
- package/dist/hud/analytics-display.js.map +1 -1
- package/dist/hud/render.d.ts.map +1 -1
- package/dist/hud/render.js +49 -18
- package/dist/hud/render.js.map +1 -1
- package/dist/hud/types.d.ts +2 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/hud/types.js +14 -0
- package/dist/hud/types.js.map +1 -1
- package/dist/installer/index.d.ts +1 -1
- package/dist/installer/index.d.ts.map +1 -1
- package/dist/installer/index.js +4 -3
- package/dist/installer/index.js.map +1 -1
- package/hooks/hooks.json +83 -1
- package/hooks/keyword-detector.sh +4 -4
- package/hooks/persistent-mode.sh +10 -10
- package/hooks/session-start.sh +4 -4
- package/package.json +3 -1
- package/scripts/keyword-detector.mjs +4 -4
- package/scripts/permission-handler.mjs +23 -0
- package/scripts/persistent-mode.mjs +6 -6
- package/scripts/persistent-mode.sh +10 -10
- package/scripts/pre-compact.mjs +23 -0
- package/scripts/session-end.mjs +23 -0
- package/scripts/session-start.mjs +4 -4
- package/scripts/setup-init.mjs +23 -0
- package/scripts/setup-maintenance.mjs +23 -0
- package/scripts/subagent-tracker.mjs +35 -0
- package/skills/hud/SKILL.md +37 -5
- package/skills/omc-setup/SKILL.md +162 -4
- package/skills/writer-memory/SKILL.md +443 -0
- package/skills/writer-memory/lib/character-tracker.ts +338 -0
- package/skills/writer-memory/lib/memory-manager.ts +804 -0
- package/skills/writer-memory/lib/relationship-graph.ts +400 -0
- package/skills/writer-memory/lib/scene-organizer.ts +544 -0
- package/skills/writer-memory/lib/synopsis-builder.ts +339 -0
- package/skills/writer-memory/templates/synopsis-template.md +46 -0
- package/templates/hooks/keyword-detector.mjs +198 -0
- package/templates/hooks/keyword-detector.sh +102 -0
- package/templates/hooks/persistent-mode.mjs +249 -0
- package/templates/hooks/persistent-mode.sh +187 -0
- package/templates/hooks/post-tool-use.mjs +133 -0
- package/templates/hooks/post-tool-use.sh +90 -0
- package/templates/hooks/pre-tool-use.mjs +145 -0
- package/templates/hooks/pre-tool-use.sh +113 -0
- package/templates/hooks/session-start.mjs +100 -0
- package/templates/hooks/session-start.sh +62 -0
- package/templates/hooks/stop-continuation.mjs +80 -0
- package/templates/hooks/stop-continuation.sh +40 -0
- package/templates/rules/README.md +40 -0
- package/templates/rules/coding-style.md +74 -0
- package/templates/rules/git-workflow.md +41 -0
- package/templates/rules/performance.md +40 -0
- package/templates/rules/security.md +41 -0
- package/templates/rules/testing.md +42 -0
|
@@ -0,0 +1,400 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Relationship Graph Module for Writer Memory System
|
|
3
|
+
*
|
|
4
|
+
* Tracks character relationships with evolution over time,
|
|
5
|
+
* Korean relationship types, and graph-based analysis.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { loadMemory, saveMemory, generateId, now } from './memory-manager';
|
|
9
|
+
import type {
|
|
10
|
+
Relationship,
|
|
11
|
+
RelationshipType,
|
|
12
|
+
RelationshipEvent,
|
|
13
|
+
SpeechLevel,
|
|
14
|
+
WriterMemory
|
|
15
|
+
} from './memory-manager';
|
|
16
|
+
|
|
17
|
+
// ============================================================================
|
|
18
|
+
// Relationship CRUD Operations
|
|
19
|
+
// ============================================================================
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Create a new relationship between two characters
|
|
23
|
+
*
|
|
24
|
+
* @param char1Name - First character name
|
|
25
|
+
* @param char2Name - Second character name
|
|
26
|
+
* @param type - Relationship type
|
|
27
|
+
* @param options - Optional relationship properties
|
|
28
|
+
* @returns Created relationship
|
|
29
|
+
*/
|
|
30
|
+
export function addRelationship(
|
|
31
|
+
char1Name: string,
|
|
32
|
+
char2Name: string,
|
|
33
|
+
type: RelationshipType,
|
|
34
|
+
options?: {
|
|
35
|
+
dynamic?: Relationship['dynamic'];
|
|
36
|
+
speechLevel?: SpeechLevel;
|
|
37
|
+
notes?: string;
|
|
38
|
+
}
|
|
39
|
+
): Relationship | null {
|
|
40
|
+
const memory = loadMemory();
|
|
41
|
+
if (!memory) return null;
|
|
42
|
+
|
|
43
|
+
// Check if relationship already exists
|
|
44
|
+
const existing = memory.relationships.find(r =>
|
|
45
|
+
(r.from === char1Name && r.to === char2Name) ||
|
|
46
|
+
(r.from === char2Name && r.to === char1Name)
|
|
47
|
+
);
|
|
48
|
+
|
|
49
|
+
if (existing) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const relationship: Relationship = {
|
|
54
|
+
id: generateId('rel'),
|
|
55
|
+
from: char1Name,
|
|
56
|
+
to: char2Name,
|
|
57
|
+
type,
|
|
58
|
+
dynamic: options?.dynamic || 'stable',
|
|
59
|
+
speechLevel: options?.speechLevel,
|
|
60
|
+
notes: options?.notes,
|
|
61
|
+
evolution: [],
|
|
62
|
+
created: now()
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
memory.relationships.push(relationship);
|
|
66
|
+
saveMemory(memory);
|
|
67
|
+
|
|
68
|
+
return relationship;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Update an existing relationship with partial data
|
|
73
|
+
*
|
|
74
|
+
* @param char1Name - First character name
|
|
75
|
+
* @param char2Name - Second character name
|
|
76
|
+
* @param updates - Partial relationship updates
|
|
77
|
+
* @returns Updated relationship
|
|
78
|
+
*/
|
|
79
|
+
export function updateRelationship(
|
|
80
|
+
char1Name: string,
|
|
81
|
+
char2Name: string,
|
|
82
|
+
updates: Partial<Omit<Relationship, 'id' | 'from' | 'to' | 'created'>>
|
|
83
|
+
): Relationship | null {
|
|
84
|
+
const memory = loadMemory();
|
|
85
|
+
if (!memory) return null;
|
|
86
|
+
|
|
87
|
+
const relationship = getRelationship(char1Name, char2Name);
|
|
88
|
+
|
|
89
|
+
if (!relationship) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
Object.assign(relationship, updates);
|
|
94
|
+
saveMemory(memory);
|
|
95
|
+
|
|
96
|
+
return relationship;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Remove a relationship between two characters
|
|
101
|
+
*
|
|
102
|
+
* @param char1Name - First character name
|
|
103
|
+
* @param char2Name - Second character name
|
|
104
|
+
*/
|
|
105
|
+
export function removeRelationship(char1Name: string, char2Name: string): boolean {
|
|
106
|
+
const memory = loadMemory();
|
|
107
|
+
if (!memory) return false;
|
|
108
|
+
|
|
109
|
+
const index = memory.relationships.findIndex(r =>
|
|
110
|
+
(r.from === char1Name && r.to === char2Name) ||
|
|
111
|
+
(r.from === char2Name && r.to === char1Name)
|
|
112
|
+
);
|
|
113
|
+
|
|
114
|
+
if (index === -1) {
|
|
115
|
+
return false;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
memory.relationships.splice(index, 1);
|
|
119
|
+
saveMemory(memory);
|
|
120
|
+
return true;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Get relationship between two characters (direction-agnostic)
|
|
125
|
+
*
|
|
126
|
+
* @param char1Name - First character name
|
|
127
|
+
* @param char2Name - Second character name
|
|
128
|
+
* @returns Relationship or undefined
|
|
129
|
+
*/
|
|
130
|
+
export function getRelationship(char1Name: string, char2Name: string): Relationship | undefined {
|
|
131
|
+
const memory = loadMemory();
|
|
132
|
+
if (!memory) return undefined;
|
|
133
|
+
|
|
134
|
+
return memory.relationships.find(r =>
|
|
135
|
+
(r.from === char1Name && r.to === char2Name) ||
|
|
136
|
+
(r.from === char2Name && r.to === char1Name)
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
/**
|
|
141
|
+
* List all relationships, optionally filtered by character
|
|
142
|
+
*
|
|
143
|
+
* @param characterName - Optional character to filter by
|
|
144
|
+
* @returns Array of relationships
|
|
145
|
+
*/
|
|
146
|
+
export function listRelationships(characterName?: string): Relationship[] {
|
|
147
|
+
const memory = loadMemory();
|
|
148
|
+
if (!memory) return [];
|
|
149
|
+
|
|
150
|
+
if (!characterName) {
|
|
151
|
+
return memory.relationships;
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return memory.relationships.filter(r =>
|
|
155
|
+
r.from === characterName || r.to === characterName
|
|
156
|
+
);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// ============================================================================
|
|
160
|
+
// Relationship Evolution
|
|
161
|
+
// ============================================================================
|
|
162
|
+
|
|
163
|
+
/**
|
|
164
|
+
* Add a timeline event to a relationship
|
|
165
|
+
*
|
|
166
|
+
* @param char1Name - First character name
|
|
167
|
+
* @param char2Name - Second character name
|
|
168
|
+
* @param change - Description of relationship change
|
|
169
|
+
* @param catalyst - What caused the change
|
|
170
|
+
* @param sceneId - Optional scene reference
|
|
171
|
+
* @returns Created event
|
|
172
|
+
*/
|
|
173
|
+
export function addRelationshipEvent(
|
|
174
|
+
char1Name: string,
|
|
175
|
+
char2Name: string,
|
|
176
|
+
change: string,
|
|
177
|
+
catalyst: string,
|
|
178
|
+
sceneId?: string
|
|
179
|
+
): RelationshipEvent | null {
|
|
180
|
+
const relationship = getRelationship(char1Name, char2Name);
|
|
181
|
+
|
|
182
|
+
if (!relationship) {
|
|
183
|
+
return null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const event: RelationshipEvent = {
|
|
187
|
+
timestamp: now(),
|
|
188
|
+
change,
|
|
189
|
+
catalyst,
|
|
190
|
+
sceneId
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
relationship.evolution.push(event);
|
|
194
|
+
|
|
195
|
+
const memory = loadMemory();
|
|
196
|
+
if (!memory) return null;
|
|
197
|
+
|
|
198
|
+
saveMemory(memory);
|
|
199
|
+
|
|
200
|
+
return event;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
/**
|
|
204
|
+
* Get all timeline events for a relationship
|
|
205
|
+
*
|
|
206
|
+
* @param char1Name - First character name
|
|
207
|
+
* @param char2Name - Second character name
|
|
208
|
+
* @returns Array of events sorted by timestamp
|
|
209
|
+
*/
|
|
210
|
+
export function getRelationshipTimeline(char1Name: string, char2Name: string): RelationshipEvent[] {
|
|
211
|
+
const relationship = getRelationship(char1Name, char2Name);
|
|
212
|
+
|
|
213
|
+
if (!relationship) {
|
|
214
|
+
return [];
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
return relationship.evolution.sort((a, b) => a.timestamp.localeCompare(b.timestamp));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
/**
|
|
221
|
+
* Get relationship arc summary (e.g., "첫만남 → 오해 → 화해")
|
|
222
|
+
*
|
|
223
|
+
* @param char1Name - First character name
|
|
224
|
+
* @param char2Name - Second character name
|
|
225
|
+
* @returns Arc summary string
|
|
226
|
+
*/
|
|
227
|
+
export function getRelationshipArc(char1Name: string, char2Name: string): string {
|
|
228
|
+
const timeline = getRelationshipTimeline(char1Name, char2Name);
|
|
229
|
+
|
|
230
|
+
if (timeline.length === 0) {
|
|
231
|
+
return '변화 없음';
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
return timeline.map(e => e.change).join(' → ');
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// ============================================================================
|
|
238
|
+
// Graph Operations
|
|
239
|
+
// ============================================================================
|
|
240
|
+
|
|
241
|
+
/**
|
|
242
|
+
* Get all connections for a character with direction info
|
|
243
|
+
*
|
|
244
|
+
* @param characterName - Character name
|
|
245
|
+
* @returns Connections with direction (outgoing/incoming/mutual)
|
|
246
|
+
*/
|
|
247
|
+
export function getCharacterConnections(characterName: string): Array<{
|
|
248
|
+
relationship: Relationship;
|
|
249
|
+
direction: 'outgoing' | 'incoming' | 'mutual';
|
|
250
|
+
otherCharacter: string;
|
|
251
|
+
}> {
|
|
252
|
+
const relationships = listRelationships(characterName);
|
|
253
|
+
|
|
254
|
+
return relationships.map(r => {
|
|
255
|
+
const isFrom = r.from === characterName;
|
|
256
|
+
return {
|
|
257
|
+
relationship: r,
|
|
258
|
+
direction: 'mutual' as const, // Most relationships are bidirectional
|
|
259
|
+
otherCharacter: isFrom ? r.to : r.from
|
|
260
|
+
};
|
|
261
|
+
});
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
/**
|
|
265
|
+
* Get full relationship graph
|
|
266
|
+
*
|
|
267
|
+
* @returns Graph with nodes (characters) and edges (relationships)
|
|
268
|
+
*/
|
|
269
|
+
export function getRelationshipWeb(): {
|
|
270
|
+
nodes: string[];
|
|
271
|
+
edges: Array<{ from: string; to: string; type: RelationshipType }>
|
|
272
|
+
} {
|
|
273
|
+
const memory = loadMemory();
|
|
274
|
+
if (!memory) return { nodes: [], edges: [] };
|
|
275
|
+
|
|
276
|
+
const nodes = new Set<string>();
|
|
277
|
+
const edges: Array<{ from: string; to: string; type: RelationshipType }> = [];
|
|
278
|
+
|
|
279
|
+
memory.relationships.forEach(r => {
|
|
280
|
+
nodes.add(r.from);
|
|
281
|
+
nodes.add(r.to);
|
|
282
|
+
edges.push({ from: r.from, to: r.to, type: r.type });
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
return {
|
|
286
|
+
nodes: Array.from(nodes),
|
|
287
|
+
edges
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
// ============================================================================
|
|
292
|
+
// Korean Labels
|
|
293
|
+
// ============================================================================
|
|
294
|
+
|
|
295
|
+
/**
|
|
296
|
+
* Get Korean label for relationship type
|
|
297
|
+
*
|
|
298
|
+
* @param type - Relationship type
|
|
299
|
+
* @returns Korean label
|
|
300
|
+
*/
|
|
301
|
+
export function getKoreanRelationType(type: RelationshipType): string {
|
|
302
|
+
const labels: Record<RelationshipType, string> = {
|
|
303
|
+
romantic: '연인',
|
|
304
|
+
familial: '가족',
|
|
305
|
+
friendship: '우정',
|
|
306
|
+
antagonistic: '적대',
|
|
307
|
+
professional: '직업적',
|
|
308
|
+
mentor: '사제',
|
|
309
|
+
complex: '복합적'
|
|
310
|
+
};
|
|
311
|
+
|
|
312
|
+
return labels[type];
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// ============================================================================
|
|
316
|
+
// Profile Generation
|
|
317
|
+
// ============================================================================
|
|
318
|
+
|
|
319
|
+
/**
|
|
320
|
+
* Generate markdown profile for a relationship
|
|
321
|
+
*
|
|
322
|
+
* @param char1Name - First character name
|
|
323
|
+
* @param char2Name - Second character name
|
|
324
|
+
* @returns Markdown profile
|
|
325
|
+
*/
|
|
326
|
+
export function generateRelationshipProfile(char1Name: string, char2Name: string): string {
|
|
327
|
+
const relationship = getRelationship(char1Name, char2Name);
|
|
328
|
+
|
|
329
|
+
if (!relationship) {
|
|
330
|
+
return `# ${char1Name} ↔ ${char2Name}\n\n관계 정보 없음`;
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
const timeline = getRelationshipTimeline(char1Name, char2Name);
|
|
334
|
+
const arc = getRelationshipArc(char1Name, char2Name);
|
|
335
|
+
|
|
336
|
+
let profile = `# ${char1Name} ↔ ${char2Name}\n\n`;
|
|
337
|
+
profile += `**관계 유형**: ${getKoreanRelationType(relationship.type)}\n`;
|
|
338
|
+
profile += `**상태**: ${relationship.dynamic}\n`;
|
|
339
|
+
|
|
340
|
+
if (relationship.speechLevel) {
|
|
341
|
+
profile += `**말투**: ${relationship.speechLevel}\n`;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
if (relationship.notes) {
|
|
345
|
+
profile += `\n## 설명\n${relationship.notes}\n`;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
if (timeline.length > 0) {
|
|
349
|
+
profile += `\n## 관계 흐름\n${arc}\n\n`;
|
|
350
|
+
profile += `## 주요 사건\n`;
|
|
351
|
+
timeline.forEach(e => {
|
|
352
|
+
profile += `- **${e.change}**: ${e.catalyst}`;
|
|
353
|
+
if (e.sceneId) {
|
|
354
|
+
profile += ` (${e.sceneId})`;
|
|
355
|
+
}
|
|
356
|
+
profile += '\n';
|
|
357
|
+
});
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
return profile;
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
/**
|
|
364
|
+
* Generate ASCII map of all relationships with symbols
|
|
365
|
+
*
|
|
366
|
+
* @returns ASCII relationship map
|
|
367
|
+
*/
|
|
368
|
+
export function generateRelationshipMap(): string {
|
|
369
|
+
const web = getRelationshipWeb();
|
|
370
|
+
|
|
371
|
+
if (web.nodes.length === 0) {
|
|
372
|
+
return '관계 없음';
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
const symbols: Record<RelationshipType, string> = {
|
|
376
|
+
romantic: '♥',
|
|
377
|
+
familial: '家',
|
|
378
|
+
friendship: '友',
|
|
379
|
+
antagonistic: '敵',
|
|
380
|
+
professional: '職',
|
|
381
|
+
mentor: '師',
|
|
382
|
+
complex: '複'
|
|
383
|
+
};
|
|
384
|
+
|
|
385
|
+
let map = '# 관계 지도\n\n';
|
|
386
|
+
|
|
387
|
+
web.nodes.forEach(node => {
|
|
388
|
+
const connections = getCharacterConnections(node);
|
|
389
|
+
if (connections.length > 0) {
|
|
390
|
+
map += `${node}:\n`;
|
|
391
|
+
connections.forEach(conn => {
|
|
392
|
+
const symbol = symbols[conn.relationship.type];
|
|
393
|
+
map += ` ${symbol} ${conn.otherCharacter} (${getKoreanRelationType(conn.relationship.type)})\n`;
|
|
394
|
+
});
|
|
395
|
+
map += '\n';
|
|
396
|
+
}
|
|
397
|
+
});
|
|
398
|
+
|
|
399
|
+
return map;
|
|
400
|
+
}
|