@su-record/vibe 2.4.24 โ 2.4.26
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/.claude/settings.json +49 -0
- package/.claude/settings.local.json +25 -1
- package/.claude/vibe/rules/languages/dart-flutter.md +509 -0
- package/.claude/vibe/rules/languages/go.md +396 -0
- package/.claude/vibe/rules/languages/java-spring.md +586 -0
- package/.claude/vibe/rules/languages/kotlin-android.md +491 -0
- package/.claude/vibe/rules/languages/python-django.md +371 -0
- package/.claude/vibe/rules/languages/python-fastapi.md +386 -0
- package/.claude/vibe/rules/languages/rust.md +425 -0
- package/.claude/vibe/rules/languages/swift-ios.md +516 -0
- package/.claude/vibe/rules/languages/typescript-nextjs.md +441 -0
- package/.claude/vibe/rules/languages/typescript-nuxt.md +521 -0
- package/.claude/vibe/rules/languages/typescript-react-native.md +446 -0
- package/.claude/vibe/rules/languages/typescript-react.md +525 -0
- package/.claude/vibe/rules/languages/typescript-vue.md +353 -0
- package/CLAUDE.md +0 -18
- package/hooks/hooks.json +18 -18
- package/package.json +1 -1
- package/hooks/scripts/code-quality-hook.mjs +0 -17
- package/hooks/scripts/complexity-hook.mjs +0 -17
- package/hooks/scripts/context-save-hook.mjs +0 -29
- package/hooks/scripts/gemini-code-hook.mjs +0 -28
- package/hooks/scripts/gemini-hook.mjs +0 -26
- package/hooks/scripts/gemini-uiux-hook.mjs +0 -28
- package/hooks/scripts/gpt-architect-hook.mjs +0 -28
- package/hooks/scripts/gpt-debug-hook.mjs +0 -28
- package/hooks/scripts/gpt-hook.mjs +0 -26
- package/hooks/scripts/memory-list-hook.mjs +0 -18
- package/hooks/scripts/memory-save-hook.mjs +0 -19
- package/hooks/scripts/post-tool-use-hook.mjs +0 -17
- package/hooks/scripts/session-start-hook.mjs +0 -26
|
@@ -0,0 +1,353 @@
|
|
|
1
|
+
# ๐ข TypeScript + Vue/Nuxt ํ์ง ๊ท์น
|
|
2
|
+
|
|
3
|
+
## ํต์ฌ ์์น (core์์ ์์)
|
|
4
|
+
|
|
5
|
+
```markdown
|
|
6
|
+
โ
๋จ์ผ ์ฑ
์ (SRP)
|
|
7
|
+
โ
์ค๋ณต ์ ๊ฑฐ (DRY)
|
|
8
|
+
โ
์ฌ์ฌ์ฉ์ฑ
|
|
9
|
+
โ
๋ฎ์ ๋ณต์ก๋
|
|
10
|
+
โ
ํจ์ โค 30์ค, Template โค 100์ค
|
|
11
|
+
โ
์ค์ฒฉ โค 3๋จ๊ณ
|
|
12
|
+
โ
Cyclomatic complexity โค 10
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Vue 3 + TypeScript ํนํ ๊ท์น
|
|
16
|
+
|
|
17
|
+
### 1. Composition API ์ฌ์ฉ (Options API ์ง์)
|
|
18
|
+
|
|
19
|
+
```typescript
|
|
20
|
+
// โ Options API (๋ ๊ฑฐ์)
|
|
21
|
+
export default {
|
|
22
|
+
data() {
|
|
23
|
+
return { count: 0 };
|
|
24
|
+
},
|
|
25
|
+
methods: {
|
|
26
|
+
increment() {
|
|
27
|
+
this.count++;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
// โ
Composition API + script setup
|
|
33
|
+
<script setup lang="ts">
|
|
34
|
+
import { ref, computed, onMounted } from 'vue';
|
|
35
|
+
|
|
36
|
+
const count = ref(0);
|
|
37
|
+
const doubled = computed(() => count.value * 2);
|
|
38
|
+
|
|
39
|
+
function increment() {
|
|
40
|
+
count.value++;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
onMounted(() => {
|
|
44
|
+
console.log('์ปดํฌ๋ํธ ๋ง์ดํธ๋จ');
|
|
45
|
+
});
|
|
46
|
+
</script>
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
### 2. ํ์
์์ ํ Props/Emits
|
|
50
|
+
|
|
51
|
+
```typescript
|
|
52
|
+
// โ
Props ํ์
์ ์
|
|
53
|
+
interface Props {
|
|
54
|
+
userId: string;
|
|
55
|
+
title?: string;
|
|
56
|
+
items: Item[];
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
60
|
+
title: '๊ธฐ๋ณธ ์ ๋ชฉ',
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
// โ
Emits ํ์
์ ์
|
|
64
|
+
interface Emits {
|
|
65
|
+
(e: 'update', value: string): void;
|
|
66
|
+
(e: 'delete', id: number): void;
|
|
67
|
+
(e: 'select', item: Item): void;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const emit = defineEmits<Emits>();
|
|
71
|
+
|
|
72
|
+
// ์ฌ์ฉ
|
|
73
|
+
emit('update', '์ ๊ฐ');
|
|
74
|
+
emit('delete', 123);
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### 3. Composables๋ก ๋ก์ง ๋ถ๋ฆฌ
|
|
78
|
+
|
|
79
|
+
```typescript
|
|
80
|
+
// โ
composables/useUser.ts
|
|
81
|
+
import { ref, computed } from 'vue';
|
|
82
|
+
import type { User } from '@/types';
|
|
83
|
+
|
|
84
|
+
export function useUser(userId: string) {
|
|
85
|
+
const user = ref<User | null>(null);
|
|
86
|
+
const isLoading = ref(false);
|
|
87
|
+
const error = ref<string | null>(null);
|
|
88
|
+
|
|
89
|
+
const fullName = computed(() =>
|
|
90
|
+
user.value ? `${user.value.firstName} ${user.value.lastName}` : ''
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
async function fetchUser() {
|
|
94
|
+
isLoading.value = true;
|
|
95
|
+
error.value = null;
|
|
96
|
+
try {
|
|
97
|
+
const response = await api.getUser(userId);
|
|
98
|
+
user.value = response.data;
|
|
99
|
+
} catch (e) {
|
|
100
|
+
error.value = '์ฌ์ฉ์๋ฅผ ๋ถ๋ฌ์ค์ง ๋ชปํ์ต๋๋ค';
|
|
101
|
+
} finally {
|
|
102
|
+
isLoading.value = false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return {
|
|
107
|
+
user,
|
|
108
|
+
isLoading,
|
|
109
|
+
error,
|
|
110
|
+
fullName,
|
|
111
|
+
fetchUser,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
// ์ปดํฌ๋ํธ์์ ์ฌ์ฉ
|
|
116
|
+
<script setup lang="ts">
|
|
117
|
+
const { user, isLoading, fetchUser } = useUser(props.userId);
|
|
118
|
+
|
|
119
|
+
onMounted(fetchUser);
|
|
120
|
+
</script>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
### 4. Pinia ์ํ ๊ด๋ฆฌ
|
|
124
|
+
|
|
125
|
+
```typescript
|
|
126
|
+
// โ
stores/user.ts
|
|
127
|
+
import { defineStore } from 'pinia';
|
|
128
|
+
import type { User } from '@/types';
|
|
129
|
+
|
|
130
|
+
interface UserState {
|
|
131
|
+
currentUser: User | null;
|
|
132
|
+
users: User[];
|
|
133
|
+
isLoading: boolean;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export const useUserStore = defineStore('user', {
|
|
137
|
+
state: (): UserState => ({
|
|
138
|
+
currentUser: null,
|
|
139
|
+
users: [],
|
|
140
|
+
isLoading: false,
|
|
141
|
+
}),
|
|
142
|
+
|
|
143
|
+
getters: {
|
|
144
|
+
isLoggedIn: (state) => !!state.currentUser,
|
|
145
|
+
userCount: (state) => state.users.length,
|
|
146
|
+
},
|
|
147
|
+
|
|
148
|
+
actions: {
|
|
149
|
+
async login(email: string, password: string) {
|
|
150
|
+
this.isLoading = true;
|
|
151
|
+
try {
|
|
152
|
+
const user = await authApi.login(email, password);
|
|
153
|
+
this.currentUser = user;
|
|
154
|
+
} finally {
|
|
155
|
+
this.isLoading = false;
|
|
156
|
+
}
|
|
157
|
+
},
|
|
158
|
+
|
|
159
|
+
logout() {
|
|
160
|
+
this.currentUser = null;
|
|
161
|
+
},
|
|
162
|
+
},
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
// Setup Store ์คํ์ผ (๊ถ์ฅ)
|
|
166
|
+
export const useUserStore = defineStore('user', () => {
|
|
167
|
+
const currentUser = ref<User | null>(null);
|
|
168
|
+
const isLoggedIn = computed(() => !!currentUser.value);
|
|
169
|
+
|
|
170
|
+
async function login(email: string, password: string) {
|
|
171
|
+
currentUser.value = await authApi.login(email, password);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return { currentUser, isLoggedIn, login };
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
### 5. Nuxt 3 ํนํ ๊ท์น
|
|
179
|
+
|
|
180
|
+
```typescript
|
|
181
|
+
// โ
Server API Routes (server/api/)
|
|
182
|
+
// server/api/users/[id].get.ts
|
|
183
|
+
export default defineEventHandler(async (event) => {
|
|
184
|
+
const id = getRouterParam(event, 'id');
|
|
185
|
+
|
|
186
|
+
if (!id) {
|
|
187
|
+
throw createError({
|
|
188
|
+
statusCode: 400,
|
|
189
|
+
message: 'ID๊ฐ ํ์ํฉ๋๋ค',
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const user = await prisma.user.findUnique({ where: { id } });
|
|
194
|
+
|
|
195
|
+
if (!user) {
|
|
196
|
+
throw createError({
|
|
197
|
+
statusCode: 404,
|
|
198
|
+
message: '์ฌ์ฉ์๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค',
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
return user;
|
|
203
|
+
});
|
|
204
|
+
|
|
205
|
+
// โ
useFetch / useAsyncData
|
|
206
|
+
<script setup lang="ts">
|
|
207
|
+
// SSR ์ง์ ๋ฐ์ดํฐ ํ์นญ
|
|
208
|
+
const { data: user, pending, error } = await useFetch<User>(
|
|
209
|
+
`/api/users/${props.userId}`
|
|
210
|
+
);
|
|
211
|
+
|
|
212
|
+
// ์บ์ฑ ํค ์ง์
|
|
213
|
+
const { data: posts } = await useAsyncData(
|
|
214
|
+
`user-${props.userId}-posts`,
|
|
215
|
+
() => $fetch(`/api/users/${props.userId}/posts`)
|
|
216
|
+
);
|
|
217
|
+
</script>
|
|
218
|
+
|
|
219
|
+
// โ
Middleware
|
|
220
|
+
// middleware/auth.ts
|
|
221
|
+
export default defineNuxtRouteMiddleware((to, from) => {
|
|
222
|
+
const { isLoggedIn } = useUserStore();
|
|
223
|
+
|
|
224
|
+
if (!isLoggedIn && to.path !== '/login') {
|
|
225
|
+
return navigateTo('/login');
|
|
226
|
+
}
|
|
227
|
+
});
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
### 6. ์ปดํฌ๋ํธ ๊ตฌ์กฐ
|
|
231
|
+
|
|
232
|
+
```vue
|
|
233
|
+
<!-- โ
๊ถ์ฅ ์ปดํฌ๋ํธ ๊ตฌ์กฐ -->
|
|
234
|
+
<script setup lang="ts">
|
|
235
|
+
// 1. ํ์
import
|
|
236
|
+
import type { User, Item } from '@/types';
|
|
237
|
+
|
|
238
|
+
// 2. ์ปดํฌ๋ํธ import
|
|
239
|
+
import UserAvatar from '@/components/UserAvatar.vue';
|
|
240
|
+
|
|
241
|
+
// 3. Props/Emits
|
|
242
|
+
interface Props {
|
|
243
|
+
user: User;
|
|
244
|
+
editable?: boolean;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const props = withDefaults(defineProps<Props>(), {
|
|
248
|
+
editable: false,
|
|
249
|
+
});
|
|
250
|
+
|
|
251
|
+
const emit = defineEmits<{
|
|
252
|
+
(e: 'update', user: User): void;
|
|
253
|
+
}>();
|
|
254
|
+
|
|
255
|
+
// 4. Composables
|
|
256
|
+
const { isLoading, save } = useUserForm();
|
|
257
|
+
|
|
258
|
+
// 5. Reactive state
|
|
259
|
+
const formData = ref({ ...props.user });
|
|
260
|
+
const isEditing = ref(false);
|
|
261
|
+
|
|
262
|
+
// 6. Computed
|
|
263
|
+
const canSave = computed(() =>
|
|
264
|
+
formData.value.name.length > 0 && !isLoading.value
|
|
265
|
+
);
|
|
266
|
+
|
|
267
|
+
// 7. Methods
|
|
268
|
+
async function handleSave() {
|
|
269
|
+
await save(formData.value);
|
|
270
|
+
emit('update', formData.value);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 8. Lifecycle
|
|
274
|
+
onMounted(() => {
|
|
275
|
+
console.log('์ปดํฌ๋ํธ ์ค๋น๋จ');
|
|
276
|
+
});
|
|
277
|
+
</script>
|
|
278
|
+
|
|
279
|
+
<template>
|
|
280
|
+
<div class="user-card">
|
|
281
|
+
<UserAvatar :src="user.avatar" />
|
|
282
|
+
<h2>{{ user.name }}</h2>
|
|
283
|
+
<button
|
|
284
|
+
v-if="editable"
|
|
285
|
+
:disabled="!canSave"
|
|
286
|
+
@click="handleSave"
|
|
287
|
+
>
|
|
288
|
+
์ ์ฅ
|
|
289
|
+
</button>
|
|
290
|
+
</div>
|
|
291
|
+
</template>
|
|
292
|
+
|
|
293
|
+
<style scoped>
|
|
294
|
+
.user-card {
|
|
295
|
+
padding: 1rem;
|
|
296
|
+
border-radius: 8px;
|
|
297
|
+
}
|
|
298
|
+
</style>
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
## ์ํฐํจํด
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
// โ v-if์ v-for ํจ๊ป ์ฌ์ฉ
|
|
305
|
+
<li v-for="user in users" v-if="user.isActive">
|
|
306
|
+
|
|
307
|
+
// โ
computed๋ก ํํฐ๋ง
|
|
308
|
+
const activeUsers = computed(() => users.value.filter(u => u.isActive));
|
|
309
|
+
<li v-for="user in activeUsers">
|
|
310
|
+
|
|
311
|
+
// โ Props ์ง์ ์์
|
|
312
|
+
props.user.name = '์ ์ด๋ฆ';
|
|
313
|
+
|
|
314
|
+
// โ
emit์ผ๋ก ๋ถ๋ชจ์๊ฒ ์๋ฆผ
|
|
315
|
+
emit('update', { ...props.user, name: '์ ์ด๋ฆ' });
|
|
316
|
+
|
|
317
|
+
// โ $refs ๋จ์ฉ
|
|
318
|
+
this.$refs.input.focus();
|
|
319
|
+
|
|
320
|
+
// โ
template ref + expose
|
|
321
|
+
const inputRef = ref<HTMLInputElement>();
|
|
322
|
+
defineExpose({ focus: () => inputRef.value?.focus() });
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
## ํ์ผ ๊ตฌ์กฐ (Nuxt 3)
|
|
326
|
+
|
|
327
|
+
```
|
|
328
|
+
project/
|
|
329
|
+
โโโ components/
|
|
330
|
+
โ โโโ ui/ # ๊ธฐ๋ณธ UI ์ปดํฌ๋ํธ
|
|
331
|
+
โ โโโ features/ # ๊ธฐ๋ฅ๋ณ ์ปดํฌ๋ํธ
|
|
332
|
+
โ โโโ layouts/ # ๋ ์ด์์ ์ปดํฌ๋ํธ
|
|
333
|
+
โโโ composables/ # Composition ํจ์
|
|
334
|
+
โโโ stores/ # Pinia ์คํ ์ด
|
|
335
|
+
โโโ server/
|
|
336
|
+
โ โโโ api/ # API ๋ผ์ฐํธ
|
|
337
|
+
โ โโโ middleware/ # ์๋ฒ ๋ฏธ๋ค์จ์ด
|
|
338
|
+
โ โโโ utils/ # ์๋ฒ ์ ํธ๋ฆฌํฐ
|
|
339
|
+
โโโ pages/ # ํ์ผ ๊ธฐ๋ฐ ๋ผ์ฐํ
|
|
340
|
+
โโโ middleware/ # ํด๋ผ์ด์ธํธ ๋ฏธ๋ค์จ์ด
|
|
341
|
+
โโโ types/ # TypeScript ํ์
|
|
342
|
+
โโโ utils/ # ์ ํธ๋ฆฌํฐ ํจ์
|
|
343
|
+
```
|
|
344
|
+
|
|
345
|
+
## ์ฒดํฌ๋ฆฌ์คํธ
|
|
346
|
+
|
|
347
|
+
- [ ] Composition API + `<script setup>` ์ฌ์ฉ
|
|
348
|
+
- [ ] Props/Emits ํ์
์ ์
|
|
349
|
+
- [ ] Composables๋ก ๋ก์ง ๋ถ๋ฆฌ
|
|
350
|
+
- [ ] Pinia Setup Store ์คํ์ผ ์ฌ์ฉ
|
|
351
|
+
- [ ] `any` ํ์
์ฌ์ฉ ๊ธ์ง
|
|
352
|
+
- [ ] v-if/v-for ๋ถ๋ฆฌ
|
|
353
|
+
- [ ] scoped ์คํ์ผ ์ฌ์ฉ
|
package/CLAUDE.md
CHANGED
|
@@ -25,24 +25,6 @@ SPEC ์ฃผ๋ AI ์ฝ๋ฉ ํ๋ ์์ํฌ (Claude Code ์ ์ฉ)
|
|
|
25
25
|
- `@ts-ignore` ๊ธ์ง โ ํ์
๋ฌธ์ ๊ทผ๋ณธ ํด๊ฒฐ
|
|
26
26
|
- ๋ชจ๋ ํจ์์ ๋ฐํ ํ์
๋ช
์
|
|
27
27
|
|
|
28
|
-
### TypeScript ๊ท์น
|
|
29
|
-
- `any` ํ์
์ฌ์ฉ ๊ธ์ง โ `unknown` + ํ์
๊ฐ๋ ์ฌ์ฉ
|
|
30
|
-
- `as any` ์บ์คํ
๊ธ์ง โ ์ ์ ํ ์ธํฐํ์ด์ค ์ ์
|
|
31
|
-
- `@ts-ignore` ๊ธ์ง โ ํ์
๋ฌธ์ ๊ทผ๋ณธ ํด๊ฒฐ
|
|
32
|
-
- ๋ชจ๋ ํจ์์ ๋ฐํ ํ์
๋ช
์
|
|
33
|
-
|
|
34
|
-
### TypeScript ๊ท์น
|
|
35
|
-
- `any` ํ์
์ฌ์ฉ ๊ธ์ง โ `unknown` + ํ์
๊ฐ๋ ์ฌ์ฉ
|
|
36
|
-
- `as any` ์บ์คํ
๊ธ์ง โ ์ ์ ํ ์ธํฐํ์ด์ค ์ ์
|
|
37
|
-
- `@ts-ignore` ๊ธ์ง โ ํ์
๋ฌธ์ ๊ทผ๋ณธ ํด๊ฒฐ
|
|
38
|
-
- ๋ชจ๋ ํจ์์ ๋ฐํ ํ์
๋ช
์
|
|
39
|
-
|
|
40
|
-
### TypeScript ๊ท์น
|
|
41
|
-
- `any` ํ์
์ฌ์ฉ ๊ธ์ง โ `unknown` + ํ์
๊ฐ๋ ์ฌ์ฉ
|
|
42
|
-
- `as any` ์บ์คํ
๊ธ์ง โ ์ ์ ํ ์ธํฐํ์ด์ค ์ ์
|
|
43
|
-
- `@ts-ignore` ๊ธ์ง โ ํ์
๋ฌธ์ ๊ทผ๋ณธ ํด๊ฒฐ
|
|
44
|
-
- ๋ชจ๋ ํจ์์ ๋ฐํ ํ์
๋ช
์
|
|
45
|
-
|
|
46
28
|
### ์๋ฌ ์ฒ๋ฆฌ ํ์
|
|
47
29
|
- try-catch ๋๋ error state ํ์
|
|
48
30
|
- ๋ก๋ฉ ์ํ ์ฒ๋ฆฌ
|
package/hooks/hooks.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"hooks": [
|
|
11
11
|
{
|
|
12
12
|
"type": "command",
|
|
13
|
-
"command": "node -e \"const
|
|
13
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const B='file:///'+R+'/@su-record/vibe/dist/tools/';const p='$CLAUDE_PROJECT_DIR';Promise.all([import(B+'memory/index.js').then(t=>t.startSession({projectPath:p})),import(B+'time/index.js').then(t=>t.getCurrentTime({format:'human',timezone:'Asia/Seoul'})),import(B+'memory/index.js').then(t=>t.listMemories({limit:5,projectPath:p}))]).then(([s,t,m])=>console.log(s.content[0].text+'\\n\\n'+t.content[0].text+'\\n\\n[Recent Memories]\\n'+m.content[0].text)).catch(e=>console.log('[Session] Error:',e.message))\""
|
|
14
14
|
}
|
|
15
15
|
]
|
|
16
16
|
}
|
|
@@ -32,7 +32,7 @@
|
|
|
32
32
|
"hooks": [
|
|
33
33
|
{
|
|
34
34
|
"type": "command",
|
|
35
|
-
"command": "node -e \"const
|
|
35
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const B='file:///'+R+'/@su-record/vibe/dist/tools/convention/index.js';const p='$CLAUDE_PROJECT_DIR';import(B).then(t=>t.validateCodeQuality({targetPath:'.',projectPath:p}).then(r=>console.log('[CODE CHECK]',r.content[0].text.split('\\n').slice(0,3).join(' | ')))).catch(()=>console.log('[AUTO-CONTINUE] Code written. Continue.'))\""
|
|
36
36
|
}
|
|
37
37
|
]
|
|
38
38
|
}
|
|
@@ -61,7 +61,7 @@
|
|
|
61
61
|
"hooks": [
|
|
62
62
|
{
|
|
63
63
|
"type": "command",
|
|
64
|
-
"command": "node -e \"const
|
|
64
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const B='file:///'+R+'/@su-record/vibe/dist/tools/memory/index.js';const p='$CLAUDE_PROJECT_DIR';import(B).then(t=>t.saveMemory({key:'solution-'+Date.now(),value:'Solution documented at '+new Date().toISOString(),category:'solution',projectPath:p}).then(r=>console.log('[COMPOUND]',r.content[0].text))).catch(e=>console.log('[COMPOUND] \\u2717 Error:',e.message))\""
|
|
65
65
|
}
|
|
66
66
|
]
|
|
67
67
|
},
|
|
@@ -70,7 +70,7 @@
|
|
|
70
70
|
"hooks": [
|
|
71
71
|
{
|
|
72
72
|
"type": "command",
|
|
73
|
-
"command": "node -e \"const
|
|
73
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const B='file:///'+R+'/@su-record/vibe/dist/tools/convention/index.js';const p='$CLAUDE_PROJECT_DIR';import(B).then(t=>t.validateCodeQuality({targetPath:'.',projectPath:p}).then(r=>console.log('[CODE REVIEW]',r.content[0].text.split('\\n').slice(0,5).join(' | ')))).catch(e=>console.log('[CODE REVIEW] Error:',e.message))\""
|
|
74
74
|
}
|
|
75
75
|
]
|
|
76
76
|
},
|
|
@@ -88,7 +88,7 @@
|
|
|
88
88
|
"hooks": [
|
|
89
89
|
{
|
|
90
90
|
"type": "command",
|
|
91
|
-
"command": "node -e \"const
|
|
91
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const B='file:///'+R+'/@su-record/vibe/dist/tools/convention/index.js';const p='$CLAUDE_PROJECT_DIR';import(B).then(t=>t.analyzeComplexity({targetPath:'.',projectPath:p}).then(r=>console.log('[COMPLEXITY]',r.content[0].text.split('\\n').slice(0,5).join(' | ')))).catch(e=>console.log('[COMPLEXITY] Error:',e.message))\""
|
|
92
92
|
}
|
|
93
93
|
]
|
|
94
94
|
},
|
|
@@ -97,7 +97,7 @@
|
|
|
97
97
|
"hooks": [
|
|
98
98
|
{
|
|
99
99
|
"type": "command",
|
|
100
|
-
"command": "node -e \"const
|
|
100
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const B='file:///'+R+'/@su-record/vibe/dist/tools/memory/index.js';const p='$CLAUDE_PROJECT_DIR';import(B).then(t=>t.listMemories({limit:10,projectPath:p}).then(r=>console.log('[RECALL] \\u2713 Found '+r.content[0].text.split('\\n').length+' memories:',r.content[0].text.split('\\n').slice(0,7).join(' | ')))).catch(e=>console.log('[RECALL] Error:',e.message))\""
|
|
101
101
|
}
|
|
102
102
|
]
|
|
103
103
|
},
|
|
@@ -106,7 +106,7 @@
|
|
|
106
106
|
"hooks": [
|
|
107
107
|
{
|
|
108
108
|
"type": "command",
|
|
109
|
-
"command": "node -e \"const
|
|
109
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const V='file:///'+R+'/@su-record/vibe/dist/lib/gpt-api.js';let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const q=JSON.parse(d).prompt;import(V).then(g=>g.chat({messages:[{role:'user',content:q}],systemPrompt:'You are a software architect. Analyze and review the architecture.'})).then(r=>console.log('GPT-5.2 ์๋ต:',r.content)).catch(e=>console.log('[GPT] Error:',e.message))})\"",
|
|
110
110
|
"statusMessage": "GPT analyzing architecture..."
|
|
111
111
|
}
|
|
112
112
|
]
|
|
@@ -116,28 +116,28 @@
|
|
|
116
116
|
"hooks": [
|
|
117
117
|
{
|
|
118
118
|
"type": "command",
|
|
119
|
-
"command": "node -e \"const
|
|
119
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const V='file:///'+R+'/@su-record/vibe/dist/lib/gemini-api.js';let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const q=JSON.parse(d).prompt;import(V).then(g=>g.chat({messages:[{role:'user',content:q}],systemPrompt:'You are a UI/UX expert. Analyze and provide feedback.'})).then(r=>console.log('Gemini-3 ์๋ต:',r.content)).catch(e=>console.log('[Gemini] Error:',e.message))})\"",
|
|
120
120
|
"statusMessage": "Gemini reviewing UI/UX..."
|
|
121
121
|
}
|
|
122
122
|
]
|
|
123
123
|
},
|
|
124
124
|
{
|
|
125
|
-
"matcher": "
|
|
125
|
+
"matcher": "(?!.*gemini)(gpt\\s*ํํ
|gpt\\s*์๊ฒ|gpt์\\s*๋ฌผ์ด|gpt.*์ง๋ฌธ|ask\\s*gpt)",
|
|
126
126
|
"hooks": [
|
|
127
127
|
{
|
|
128
128
|
"type": "command",
|
|
129
|
-
"command": "node -e \"const
|
|
129
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const V='file:///'+R+'/@su-record/vibe/dist/lib/gpt-api.js';let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const q=JSON.parse(d).prompt.replace(/gpt.*?(๋ฌผ์ด|์ง๋ฌธ|ํํ
|๋ถ์)[^๊ฐ-ํฃ]*/i,'').trim();import(V).then(g=>g.quickAsk(q)).then(r=>console.log('GPT-5.2 ์๋ต:',r)).catch(e=>console.log('[GPT] Error:',e.message))})\"",
|
|
130
130
|
"statusMessage": "Asking GPT-5.2..."
|
|
131
131
|
}
|
|
132
132
|
]
|
|
133
133
|
},
|
|
134
134
|
{
|
|
135
|
-
"matcher": "
|
|
135
|
+
"matcher": "(?!.*gpt)(gemini\\s*ํํ
|gemini\\s*์๊ฒ|gemini์\\s*๋ฌผ์ด|gemini.*์ง๋ฌธ|ask\\s*gemini|์ ๋ฏธ๋์ด)",
|
|
136
136
|
"hooks": [
|
|
137
137
|
{
|
|
138
138
|
"type": "command",
|
|
139
|
-
"command": "node -e \"const
|
|
140
|
-
"statusMessage": "Asking Gemini
|
|
139
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const V='file:///'+R+'/@su-record/vibe/dist/lib/gemini-api.js';let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const q=JSON.parse(d).prompt.replace(/(gemini|์ ๋ฏธ๋์ด).*?(๋ฌผ์ด|์ง๋ฌธ|ํํ
|๋ถ์)[^๊ฐ-ํฃ]*/i,'').trim();import(V).then(g=>g.quickAsk(q)).then(r=>console.log('Gemini-3 ์๋ต:',r)).catch(e=>console.log('[Gemini] Error:',e.message))})\"",
|
|
140
|
+
"statusMessage": "Asking Gemini 3..."
|
|
141
141
|
}
|
|
142
142
|
]
|
|
143
143
|
},
|
|
@@ -146,7 +146,7 @@
|
|
|
146
146
|
"hooks": [
|
|
147
147
|
{
|
|
148
148
|
"type": "command",
|
|
149
|
-
"command": "node -e \"const
|
|
149
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const V='file:///'+R+'/@su-record/vibe/dist/lib/gpt-api.js';let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const q=JSON.parse(d).prompt;import(V).then(g=>g.chat({messages:[{role:'user',content:q}],systemPrompt:'You are a debugging expert. Find bugs and suggest fixes.'})).then(r=>console.log('GPT-5.2 ์๋ต:',r.content)).catch(e=>console.log('[GPT] Error:',e.message))})\"",
|
|
150
150
|
"statusMessage": "GPT debugging..."
|
|
151
151
|
}
|
|
152
152
|
]
|
|
@@ -156,7 +156,7 @@
|
|
|
156
156
|
"hooks": [
|
|
157
157
|
{
|
|
158
158
|
"type": "command",
|
|
159
|
-
"command": "node -e \"const
|
|
159
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const V='file:///'+R+'/@su-record/vibe/dist/lib/gemini-api.js';let d='';process.stdin.on('data',c=>d+=c);process.stdin.on('end',()=>{const q=JSON.parse(d).prompt;import(V).then(g=>g.chat({messages:[{role:'user',content:q}],systemPrompt:'You are a code analysis expert. Review and analyze the code.'})).then(r=>console.log('Gemini-3 ์๋ต:',r.content)).catch(e=>console.log('[Gemini] Error:',e.message))})\"",
|
|
160
160
|
"statusMessage": "Gemini analyzing code..."
|
|
161
161
|
}
|
|
162
162
|
]
|
|
@@ -168,7 +168,7 @@
|
|
|
168
168
|
"hooks": [
|
|
169
169
|
{
|
|
170
170
|
"type": "command",
|
|
171
|
-
"command": "node -e \"const
|
|
171
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const B='file:///'+R+'/@su-record/vibe/dist/tools/memory/index.js';const p='$CLAUDE_PROJECT_DIR';import(B).then(t=>t.autoSaveContext({urgency:'medium',contextType:'progress',summary:'Context at 80% - auto checkpoint',projectPath:p}).then(r=>console.log('[CONTEXT 80%]',r.content[0].text))).catch(()=>{})\""
|
|
172
172
|
}
|
|
173
173
|
]
|
|
174
174
|
},
|
|
@@ -177,7 +177,7 @@
|
|
|
177
177
|
"hooks": [
|
|
178
178
|
{
|
|
179
179
|
"type": "command",
|
|
180
|
-
"command": "node -e \"const
|
|
180
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const B='file:///'+R+'/@su-record/vibe/dist/tools/memory/index.js';const p='$CLAUDE_PROJECT_DIR';import(B).then(t=>t.autoSaveContext({urgency:'high',contextType:'progress',summary:'Context at 90% - save before overflow',projectPath:p}).then(r=>console.log('[CONTEXT 90%]',r.content[0].text))).catch(()=>{})\""
|
|
181
181
|
}
|
|
182
182
|
]
|
|
183
183
|
},
|
|
@@ -186,7 +186,7 @@
|
|
|
186
186
|
"hooks": [
|
|
187
187
|
{
|
|
188
188
|
"type": "command",
|
|
189
|
-
"command": "node -e \"const
|
|
189
|
+
"command": "node -e \"const{execSync:X}=require('child_process');const R=process.platform==='win32'?process.env.APPDATA.replace(/\\\\/g,'/')+'/npm/node_modules':X('npm root -g').toString().trim();const B='file:///'+R+'/@su-record/vibe/dist/tools/memory/index.js';const p='$CLAUDE_PROJECT_DIR';import(B).then(t=>t.autoSaveContext({urgency:'critical',contextType:'progress',summary:'Context at 95% - CRITICAL save before session end',projectPath:p}).then(r=>console.log('[CONTEXT 95%]',r.content[0].text))).catch(()=>{})\""
|
|
190
190
|
}
|
|
191
191
|
]
|
|
192
192
|
}
|
package/package.json
CHANGED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { fileURLToPath } from "url";
|
|
3
|
-
import { dirname, resolve } from "path";
|
|
4
|
-
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const m = await import("file://" + resolve(__dirname, "../../dist/tools/convention/index.js"));
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
const projectPath = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
10
|
-
const r = await m.validateCodeQuality({
|
|
11
|
-
targetPath: ".",
|
|
12
|
-
projectPath
|
|
13
|
-
});
|
|
14
|
-
console.log("[CODE REVIEW]", r.content[0].text.split("\n").slice(0, 5).join(" | "));
|
|
15
|
-
} catch (e) {
|
|
16
|
-
console.log("[CODE REVIEW] Error:", e.message);
|
|
17
|
-
}
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { fileURLToPath } from "url";
|
|
3
|
-
import { dirname, resolve } from "path";
|
|
4
|
-
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const m = await import("file://" + resolve(__dirname, "../../dist/tools/convention/index.js"));
|
|
7
|
-
|
|
8
|
-
try {
|
|
9
|
-
const projectPath = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
10
|
-
const r = await m.analyzeComplexity({
|
|
11
|
-
targetPath: ".",
|
|
12
|
-
projectPath
|
|
13
|
-
});
|
|
14
|
-
console.log("[COMPLEXITY]", r.content[0].text.split("\n").slice(0, 5).join(" | "));
|
|
15
|
-
} catch (e) {
|
|
16
|
-
console.log("[COMPLEXITY] Error:", e.message);
|
|
17
|
-
}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { fileURLToPath } from "url";
|
|
3
|
-
import { dirname, resolve } from "path";
|
|
4
|
-
|
|
5
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
6
|
-
const m = await import("file://" + resolve(__dirname, "../../dist/tools/memory/index.js"));
|
|
7
|
-
|
|
8
|
-
// Get urgency from args: node context-save-hook.mjs 80|90|95
|
|
9
|
-
const urgencyLevel = process.argv[2] || "80";
|
|
10
|
-
const urgencyMap = {
|
|
11
|
-
"80": { urgency: "medium", summary: "Context at 80% - auto checkpoint" },
|
|
12
|
-
"90": { urgency: "high", summary: "Context at 90% - save before overflow" },
|
|
13
|
-
"95": { urgency: "critical", summary: "Context at 95% - CRITICAL save before session end" }
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
const config = urgencyMap[urgencyLevel] || urgencyMap["80"];
|
|
17
|
-
|
|
18
|
-
try {
|
|
19
|
-
const projectPath = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
20
|
-
const r = await m.autoSaveContext({
|
|
21
|
-
urgency: config.urgency,
|
|
22
|
-
contextType: "progress",
|
|
23
|
-
summary: config.summary,
|
|
24
|
-
projectPath
|
|
25
|
-
});
|
|
26
|
-
console.log(`[CONTEXT ${urgencyLevel}%]`, r.content[0].text);
|
|
27
|
-
} catch (e) {
|
|
28
|
-
// Silent fail for context save
|
|
29
|
-
}
|
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { fileURLToPath } from "url";
|
|
3
|
-
import { dirname, resolve } from "path";
|
|
4
|
-
|
|
5
|
-
// Read stdin
|
|
6
|
-
let data = "";
|
|
7
|
-
for await (const chunk of process.stdin) {
|
|
8
|
-
data += chunk;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
12
|
-
const m = await import("file://" + resolve(__dirname, "../../dist/lib/gemini-api.js"));
|
|
13
|
-
|
|
14
|
-
try {
|
|
15
|
-
const json = JSON.parse(data || "{}");
|
|
16
|
-
const prompt = json.prompt || "";
|
|
17
|
-
if (prompt) {
|
|
18
|
-
const r = await m.chat({
|
|
19
|
-
messages: [{role: "user", content: prompt}],
|
|
20
|
-
systemPrompt: "You are a code analysis expert. Review and analyze the code."
|
|
21
|
-
});
|
|
22
|
-
console.log("Gemini-3 ์๋ต:", r.content);
|
|
23
|
-
} else {
|
|
24
|
-
console.log("[Gemini] No prompt");
|
|
25
|
-
}
|
|
26
|
-
} catch (e) {
|
|
27
|
-
console.log("[Gemini] Error:", e.message);
|
|
28
|
-
}
|