@wooojin/forgen 0.4.7 → 0.4.9
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-plugin/plugin.json +1 -1
- package/CHANGELOG.md +40 -0
- package/assets/dev-guide/be/README.md +226 -0
- package/assets/dev-guide/be/adapters/build-agents-md.sh +63 -0
- package/assets/dev-guide/be/principles/common.md +433 -0
- package/assets/dev-guide/be/principles/go.md +469 -0
- package/assets/dev-guide/be/principles/node.md +388 -0
- package/assets/dev-guide/be/skills/go/be-build/SKILL.md +262 -0
- package/assets/dev-guide/be/skills/go/be-perf/SKILL.md +308 -0
- package/assets/dev-guide/be/skills/go/be-review/SKILL.md +119 -0
- package/assets/dev-guide/be/skills/go/be-security/SKILL.md +362 -0
- package/assets/dev-guide/be/skills/node/be-build/SKILL.md +239 -0
- package/assets/dev-guide/be/skills/node/be-perf/SKILL.md +272 -0
- package/assets/dev-guide/be/skills/node/be-review/SKILL.md +118 -0
- package/assets/dev-guide/be/skills/node/be-security/SKILL.md +355 -0
- package/assets/dev-guide/be/sources/12factor/INDEX.md +53 -0
- package/assets/dev-guide/be/sources/api-design/INDEX.md +56 -0
- package/assets/dev-guide/be/sources/ddia/INDEX.md +55 -0
- package/assets/dev-guide/be/sources/go-runtime/INDEX.md +62 -0
- package/assets/dev-guide/be/sources/node-runtime/INDEX.md +60 -0
- package/assets/dev-guide/be/sources/otel/INDEX.md +53 -0
- package/assets/dev-guide/be/sources/owasp-api/INDEX.md +52 -0
- package/assets/dev-guide/be/sources/postgres/INDEX.md +55 -0
- package/assets/dev-guide/be/sources/sre-book/INDEX.md +48 -0
- package/assets/dev-guide/fe/README.md +197 -0
- package/assets/dev-guide/fe/adapters/build-agents-md.sh +63 -0
- package/assets/dev-guide/fe/adapters/refresh.sh +68 -0
- package/assets/dev-guide/fe/principles/common.md +160 -0
- package/assets/dev-guide/fe/principles/react.md +183 -0
- package/assets/dev-guide/fe/principles/vue.md +196 -0
- package/assets/dev-guide/fe/skills/react/fe-build/SKILL.md +139 -0
- package/assets/dev-guide/fe/skills/react/fe-perf/SKILL.md +179 -0
- package/assets/dev-guide/fe/skills/react/fe-review/SKILL.md +141 -0
- package/assets/dev-guide/fe/skills/vue/fe-build/SKILL.md +148 -0
- package/assets/dev-guide/fe/skills/vue/fe-perf/SKILL.md +163 -0
- package/assets/dev-guide/fe/skills/vue/fe-review/SKILL.md +136 -0
- package/assets/dev-guide/fe/sources/a11y-dx/INDEX.md +41 -0
- package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-memory.md +150 -0
- package/assets/dev-guide/fe/sources/a11y-dx/chrome-devtools-performance.md +99 -0
- package/assets/dev-guide/fe/sources/a11y-dx/lighthouse-audits.md +146 -0
- package/assets/dev-guide/fe/sources/a11y-dx/react-devtools-profiler.md +128 -0
- package/assets/dev-guide/fe/sources/a11y-dx/wcag22-new-criteria.md +174 -0
- package/assets/dev-guide/fe/sources/perf/01-core-web-vitals.md +58 -0
- package/assets/dev-guide/fe/sources/perf/02-inp.md +83 -0
- package/assets/dev-guide/fe/sources/perf/03-lcp-cls.md +130 -0
- package/assets/dev-guide/fe/sources/perf/04-speculation-rules.md +148 -0
- package/assets/dev-guide/fe/sources/perf/05-view-transitions.md +153 -0
- package/assets/dev-guide/fe/sources/perf/06-nextjs-caching.md +188 -0
- package/assets/dev-guide/fe/sources/perf/07-server-components.md +181 -0
- package/assets/dev-guide/fe/sources/perf/08-ppr.md +133 -0
- package/assets/dev-guide/fe/sources/perf/09-nextjs-image.md +200 -0
- package/assets/dev-guide/fe/sources/perf/10-optimize-lcp.md +201 -0
- package/assets/dev-guide/fe/sources/perf/INDEX.md +88 -0
- package/assets/dev-guide/fe/sources/react/INDEX.md +41 -0
- package/assets/dev-guide/fe/sources/react/keeping-components-pure.md +135 -0
- package/assets/dev-guide/fe/sources/react/no-effect-patterns.md +183 -0
- package/assets/dev-guide/fe/sources/react/react-compiler.md +182 -0
- package/assets/dev-guide/fe/sources/react/server-components.md +194 -0
- package/assets/dev-guide/fe/sources/react/server-functions.md +192 -0
- package/assets/dev-guide/fe/sources/react/suspense.md +218 -0
- package/assets/dev-guide/fe/sources/react/use-action-state.md +123 -0
- package/assets/dev-guide/fe/sources/react/use-form-status.md +158 -0
- package/assets/dev-guide/fe/sources/react/use-hook.md +153 -0
- package/assets/dev-guide/fe/sources/react/use-optimistic.md +194 -0
- package/assets/dev-guide/fe/sources/toss-ff/INDEX.md +58 -0
- package/assets/dev-guide/fe/sources/toss-ff/cohesion-code-directory.md +79 -0
- package/assets/dev-guide/fe/sources/toss-ff/cohesion-form-fields.md +110 -0
- package/assets/dev-guide/fe/sources/toss-ff/cohesion-magic-number.md +47 -0
- package/assets/dev-guide/fe/sources/toss-ff/coupling-item-edit-modal.md +124 -0
- package/assets/dev-guide/fe/sources/toss-ff/coupling-use-bottom-sheet.md +57 -0
- package/assets/dev-guide/fe/sources/toss-ff/coupling-use-page-state.md +71 -0
- package/assets/dev-guide/fe/sources/toss-ff/overview-4-principles.md +77 -0
- package/assets/dev-guide/fe/sources/toss-ff/predictability-hidden-logic.md +59 -0
- package/assets/dev-guide/fe/sources/toss-ff/predictability-http.md +77 -0
- package/assets/dev-guide/fe/sources/toss-ff/predictability-use-user.md +110 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-comparison-order.md +52 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-condition-name.md +64 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-login-start-page.md +183 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-magic-number.md +53 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-submit-button.md +73 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-ternary-operator.md +38 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-use-page-state.md +77 -0
- package/assets/dev-guide/fe/sources/toss-ff/readability-user-policy.md +98 -0
- package/assets/dev-guide/fe/sources/vue/INDEX.md +17 -0
- package/assets/dev-guide/fe/sources/vue/composition-api.md +251 -0
- package/assets/dev-guide/fe/sources/vue/nuxt-data-fetching.md +232 -0
- package/assets/dev-guide/fe/sources/vue/pinia-state-management.md +134 -0
- package/assets/dev-guide/fe/sources/vue/reactivity-pitfalls.md +261 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-a.md +117 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-b.md +231 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-c.md +86 -0
- package/assets/dev-guide/fe/sources/vue/style-guide-priority-d.md +72 -0
- package/dist/checks/self-score-deflation.js +6 -4
- package/dist/cli.js +47 -2
- package/dist/core/auto-compound-runner.js +6 -2
- package/dist/core/dashboard-cli.d.ts +12 -0
- package/dist/core/dashboard-cli.js +226 -0
- package/dist/core/dashboard.js +2 -2
- package/dist/core/dev-guide-injector.d.ts +26 -0
- package/dist/core/dev-guide-injector.js +137 -0
- package/dist/core/doctor.d.ts +10 -0
- package/dist/core/doctor.js +49 -8
- package/dist/core/harness.js +8 -2
- package/dist/core/init.js +53 -0
- package/dist/core/inspect-cli.js +4 -4
- package/dist/core/lifecycle-classifier.d.ts +23 -0
- package/dist/core/lifecycle-classifier.js +104 -0
- package/dist/core/migrate-evidence-host.js +1 -1
- package/dist/core/notify.js +7 -0
- package/dist/core/observability-backfill.d.ts +31 -0
- package/dist/core/observability-backfill.js +178 -0
- package/dist/core/observability-store.d.ts +58 -0
- package/dist/core/observability-store.js +195 -0
- package/dist/core/paths.d.ts +16 -2
- package/dist/core/paths.js +16 -2
- package/dist/core/session-store.d.ts +12 -1
- package/dist/core/session-store.js +77 -1
- package/dist/core/spawn.d.ts +17 -0
- package/dist/core/spawn.js +191 -8
- package/dist/core/statusline-cli.js +34 -1
- package/dist/core/v1-bootstrap.d.ts +7 -0
- package/dist/core/v1-bootstrap.js +28 -6
- package/dist/engine/compound-extractor.js +40 -1
- package/dist/engine/compound-loop.js +6 -0
- package/dist/engine/compound-retire.d.ts +20 -0
- package/dist/engine/compound-retire.js +85 -0
- package/dist/engine/learn-cli.js +2 -2
- package/dist/engine/lifecycle/bypass-detector.js +3 -2
- package/dist/engine/lifecycle/meta-reclassifier.js +1 -1
- package/dist/engine/lifecycle/signals.js +2 -2
- package/dist/engine/lifecycle/trigger-t1-correction.js +1 -1
- package/dist/engine/solution-candidate.js +1 -1
- package/dist/engine/solution-outcomes.js +1 -1
- package/dist/engine/solution-quarantine.js +1 -1
- package/dist/engine/solution-weakness.js +8 -2
- package/dist/forge/cli.js +1 -1
- package/dist/hooks/context-guard.js +25 -1
- package/dist/hooks/keyword-detector.js +1 -1
- package/dist/hooks/post-tool-use.js +48 -0
- package/dist/hooks/secret-filter.js +2 -2
- package/dist/hooks/shared/hook-response.js +1 -1
- package/dist/hooks/shared/hook-timing.js +3 -3
- package/dist/hooks/solution-injector.js +94 -1
- package/dist/hooks/stop-guard.js +3 -3
- package/dist/host/install-claude.d.ts +6 -2
- package/dist/host/install-claude.js +74 -2
- package/dist/host/install-codex.d.ts +4 -0
- package/dist/host/install-codex.js +72 -1
- package/dist/host/install-orchestrator.js +1 -0
- package/dist/mcp/tools.js +1 -1
- package/dist/preset/facet-catalog.js +2 -2
- package/dist/renderer/rule-renderer.js +7 -7
- package/dist/store/compound-usage-store.js +1 -1
- package/dist/store/implicit-feedback-store.js +2 -2
- package/dist/store/profile-store.d.ts +11 -0
- package/dist/store/profile-store.js +23 -0
- package/package.json +6 -6
- package/plugin.json +1 -1
- package/scripts/postinstall.js +134 -0
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Vue 3 스타일 가이드 - Priority A (Essential)
|
|
3
|
+
source: https://vuejs.org/style-guide/rules-essential
|
|
4
|
+
fetched: 2026-05-18
|
|
5
|
+
category: style-guide
|
|
6
|
+
vue_version: 3
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Priority A: Essential
|
|
10
|
+
|
|
11
|
+
반드시 지켜야 하는 규칙. 위반 시 버그 또는 예측 불가 동작이 발생한다.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. 멀티-워드 컴포넌트 이름
|
|
16
|
+
|
|
17
|
+
**규칙:** 루트 `App` 컴포넌트를 제외한 모든 사용자 정의 컴포넌트는 반드시 다단어 이름을 사용한다. HTML 요소는 모두 단일 단어이므로 충돌을 방지한다.
|
|
18
|
+
|
|
19
|
+
```vue
|
|
20
|
+
<!-- Bad -->
|
|
21
|
+
<Item />
|
|
22
|
+
<!-- Good -->
|
|
23
|
+
<TodoItem />
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
---
|
|
27
|
+
|
|
28
|
+
## 2. 상세한 Prop 정의
|
|
29
|
+
|
|
30
|
+
**규칙:** 커밋된 코드에서 prop 정의는 가능한 한 상세하게 작성하고, 최소한 타입은 지정해야 한다.
|
|
31
|
+
|
|
32
|
+
```js
|
|
33
|
+
// Bad
|
|
34
|
+
const props = defineProps(['status'])
|
|
35
|
+
|
|
36
|
+
// Good
|
|
37
|
+
const props = defineProps({
|
|
38
|
+
status: String
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
// Best
|
|
42
|
+
const props = defineProps({
|
|
43
|
+
status: {
|
|
44
|
+
type: String,
|
|
45
|
+
required: true,
|
|
46
|
+
validator: (value) => {
|
|
47
|
+
return ['syncing', 'synced', 'version-conflict', 'error'].includes(value)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
})
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
---
|
|
54
|
+
|
|
55
|
+
## 3. v-for에 key 사용
|
|
56
|
+
|
|
57
|
+
**규칙:** 컴포넌트의 `v-for`에는 반드시 `key`를 사용한다.
|
|
58
|
+
|
|
59
|
+
```vue
|
|
60
|
+
<!-- Bad -->
|
|
61
|
+
<li v-for="todo in todos">{{ todo.text }}</li>
|
|
62
|
+
|
|
63
|
+
<!-- Good -->
|
|
64
|
+
<li v-for="todo in todos" :key="todo.id">{{ todo.text }}</li>
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
---
|
|
68
|
+
|
|
69
|
+
## 4. v-if와 v-for 혼용 금지
|
|
70
|
+
|
|
71
|
+
**규칙:** 같은 요소에 `v-if`와 `v-for`를 함께 사용하지 않는다. Vue에서 `v-if`가 `v-for`보다 우선순위가 높아 반복 변수에 접근이 불가하다.
|
|
72
|
+
|
|
73
|
+
```vue
|
|
74
|
+
<!-- Bad -->
|
|
75
|
+
<li v-for="user in users" v-if="user.isActive" :key="user.id">
|
|
76
|
+
|
|
77
|
+
<!-- Good: computed로 필터링 -->
|
|
78
|
+
<li v-for="user in activeUsers" :key="user.id">
|
|
79
|
+
|
|
80
|
+
<!-- Good: template 래퍼 사용 -->
|
|
81
|
+
<template v-for="user in users" :key="user.id">
|
|
82
|
+
<li v-if="user.isActive">{{ user.name }}</li>
|
|
83
|
+
</template>
|
|
84
|
+
```
|
|
85
|
+
|
|
86
|
+
```js
|
|
87
|
+
// Composition API
|
|
88
|
+
const activeUsers = computed(() => users.filter((user) => user.isActive))
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 5. 컴포넌트 스코프 스타일링
|
|
94
|
+
|
|
95
|
+
**규칙:** 앱 레벨 및 레이아웃 컴포넌트를 제외한 모든 컴포넌트의 스타일은 반드시 스코프를 가져야 한다.
|
|
96
|
+
|
|
97
|
+
```vue
|
|
98
|
+
<!-- Bad: 전역 스타일 -->
|
|
99
|
+
<style>
|
|
100
|
+
.btn-close { background-color: red; }
|
|
101
|
+
</style>
|
|
102
|
+
|
|
103
|
+
<!-- Good: scoped -->
|
|
104
|
+
<style scoped>
|
|
105
|
+
.btn-close { background-color: red; }
|
|
106
|
+
</style>
|
|
107
|
+
|
|
108
|
+
<!-- Good: CSS Modules -->
|
|
109
|
+
<style module>
|
|
110
|
+
.btnClose { background-color: red; }
|
|
111
|
+
</style>
|
|
112
|
+
|
|
113
|
+
<!-- Good: BEM -->
|
|
114
|
+
<style>
|
|
115
|
+
.c-Button--close { background-color: red; }
|
|
116
|
+
</style>
|
|
117
|
+
```
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Vue 3 스타일 가이드 - Priority B (Strongly Recommended)
|
|
3
|
+
source: https://vuejs.org/style-guide/rules-strongly-recommended
|
|
4
|
+
fetched: 2026-05-18
|
|
5
|
+
category: style-guide
|
|
6
|
+
vue_version: 3
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Priority B: Strongly Recommended
|
|
10
|
+
|
|
11
|
+
코드 가독성과 DX를 향상시킨다. 위반 시 코드는 동작하나 정당한 이유가 있어야 한다.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. 컴포넌트 파일 분리
|
|
16
|
+
|
|
17
|
+
각 컴포넌트는 별도 파일로 분리한다.
|
|
18
|
+
|
|
19
|
+
```
|
|
20
|
+
components/
|
|
21
|
+
|- TodoList.vue
|
|
22
|
+
|- TodoItem.vue
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
---
|
|
26
|
+
|
|
27
|
+
## 2. SFC 파일명 케이싱
|
|
28
|
+
|
|
29
|
+
PascalCase 또는 kebab-case 중 하나를 일관되게 사용한다.
|
|
30
|
+
|
|
31
|
+
```
|
|
32
|
+
# Good (PascalCase)
|
|
33
|
+
components/|- MyComponent.vue
|
|
34
|
+
|
|
35
|
+
# Good (kebab-case)
|
|
36
|
+
components/|- my-component.vue
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
---
|
|
40
|
+
|
|
41
|
+
## 3. 베이스 컴포넌트 이름 접두사
|
|
42
|
+
|
|
43
|
+
순수 프레젠테이션 컴포넌트는 `Base`, `App`, `V` 접두사를 붙인다.
|
|
44
|
+
|
|
45
|
+
```
|
|
46
|
+
# Bad
|
|
47
|
+
|- MyButton.vue, Icon.vue
|
|
48
|
+
|
|
49
|
+
# Good
|
|
50
|
+
|- BaseButton.vue, BaseTable.vue, BaseIcon.vue
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
Vite 자동 전역 등록:
|
|
54
|
+
```js
|
|
55
|
+
const modules = import.meta.glob('./src/**/Base*.vue', { eager: true })
|
|
56
|
+
for (const path in modules) {
|
|
57
|
+
const config = modules[path].default
|
|
58
|
+
const name = config.name || path.match(/Base[A-Z]\w+/)[0]
|
|
59
|
+
app.component(name, config)
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
---
|
|
64
|
+
|
|
65
|
+
## 4. 강하게 결합된 컴포넌트 이름
|
|
66
|
+
|
|
67
|
+
부모에 종속된 자식 컴포넌트는 부모 이름을 접두사로 사용한다.
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
# Bad
|
|
71
|
+
|- TodoList.vue, TodoItem.vue, TodoButton.vue
|
|
72
|
+
|
|
73
|
+
# Good (연관성 명확)
|
|
74
|
+
|- TodoList.vue, TodoListItem.vue, TodoListItemButton.vue
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
---
|
|
78
|
+
|
|
79
|
+
## 5. 컴포넌트 이름 단어 순서
|
|
80
|
+
|
|
81
|
+
가장 일반적인 단어부터 시작하고, 수식어를 뒤에 붙인다.
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
# Bad
|
|
85
|
+
|- ClearSearchButton.vue, RunSearchButton.vue
|
|
86
|
+
|
|
87
|
+
# Good
|
|
88
|
+
|- SearchButtonClear.vue, SearchButtonRun.vue, SearchInputQuery.vue
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
---
|
|
92
|
+
|
|
93
|
+
## 6. 셀프 클로징 컴포넌트
|
|
94
|
+
|
|
95
|
+
SFC/JSX에서 콘텐츠 없는 컴포넌트는 셀프 클로징한다. in-DOM 템플릿에서는 사용하지 않는다.
|
|
96
|
+
|
|
97
|
+
```vue
|
|
98
|
+
<!-- Good: SFC/JSX -->
|
|
99
|
+
<MyComponent/>
|
|
100
|
+
|
|
101
|
+
<!-- Good: in-DOM template -->
|
|
102
|
+
<my-component></my-component>
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## 7. 템플릿에서 컴포넌트 이름 케이싱
|
|
108
|
+
|
|
109
|
+
SFC: PascalCase / in-DOM: kebab-case
|
|
110
|
+
|
|
111
|
+
```vue
|
|
112
|
+
<MyComponent/> <!-- SFC -->
|
|
113
|
+
<my-component></my-component> <!-- in-DOM -->
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 8. JS/JSX에서 PascalCase
|
|
119
|
+
|
|
120
|
+
```js
|
|
121
|
+
import MyComponent from './MyComponent.vue'
|
|
122
|
+
export default { name: 'MyComponent' }
|
|
123
|
+
```
|
|
124
|
+
|
|
125
|
+
---
|
|
126
|
+
|
|
127
|
+
## 9. Prop 이름 케이싱
|
|
128
|
+
|
|
129
|
+
선언: camelCase / in-DOM 템플릿: kebab-case
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
// 선언
|
|
133
|
+
const props = defineProps({ greetingText: String })
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
```vue
|
|
137
|
+
<!-- SFC -->
|
|
138
|
+
<WelcomeMessage greeting-text="hi"/>
|
|
139
|
+
<!-- in-DOM -->
|
|
140
|
+
<welcome-message greeting-text="hi"></welcome-message>
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
---
|
|
144
|
+
|
|
145
|
+
## 10. 멀티 속성 요소 줄 분리
|
|
146
|
+
|
|
147
|
+
여러 속성은 한 줄에 하나씩 작성한다.
|
|
148
|
+
|
|
149
|
+
```vue
|
|
150
|
+
<!-- Bad -->
|
|
151
|
+
<MyComponent foo="a" bar="b" baz="c"/>
|
|
152
|
+
|
|
153
|
+
<!-- Good -->
|
|
154
|
+
<MyComponent
|
|
155
|
+
foo="a"
|
|
156
|
+
bar="b"
|
|
157
|
+
baz="c"
|
|
158
|
+
/>
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
---
|
|
162
|
+
|
|
163
|
+
## 11. 템플릿에서 단순 표현식
|
|
164
|
+
|
|
165
|
+
복잡한 로직은 computed나 method로 분리한다.
|
|
166
|
+
|
|
167
|
+
```vue
|
|
168
|
+
<!-- Bad -->
|
|
169
|
+
{{ fullName.split(' ').map((word) => word[0].toUpperCase() + word.slice(1)).join(' ') }}
|
|
170
|
+
|
|
171
|
+
<!-- Good -->
|
|
172
|
+
{{ normalizedFullName }}
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
```js
|
|
176
|
+
const normalizedFullName = computed(() =>
|
|
177
|
+
fullName.value
|
|
178
|
+
.split(' ')
|
|
179
|
+
.map((word) => word[0].toUpperCase() + word.slice(1))
|
|
180
|
+
.join(' ')
|
|
181
|
+
)
|
|
182
|
+
```
|
|
183
|
+
|
|
184
|
+
---
|
|
185
|
+
|
|
186
|
+
## 12. 단순 computed 속성
|
|
187
|
+
|
|
188
|
+
복잡한 computed는 여러 단순한 속성으로 분리한다.
|
|
189
|
+
|
|
190
|
+
```js
|
|
191
|
+
// Bad
|
|
192
|
+
const price = computed(() => {
|
|
193
|
+
const basePrice = manufactureCost.value / (1 - profitMargin.value)
|
|
194
|
+
return basePrice - basePrice * (discountPercent.value || 0)
|
|
195
|
+
})
|
|
196
|
+
|
|
197
|
+
// Good
|
|
198
|
+
const basePrice = computed(() => manufactureCost.value / (1 - profitMargin.value))
|
|
199
|
+
const discount = computed(() => basePrice.value * (discountPercent.value || 0))
|
|
200
|
+
const finalPrice = computed(() => basePrice.value - discount.value)
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
---
|
|
204
|
+
|
|
205
|
+
## 13. 속성 값 인용
|
|
206
|
+
|
|
207
|
+
비어있지 않은 HTML 속성 값은 항상 따옴표로 감싼다.
|
|
208
|
+
|
|
209
|
+
```vue
|
|
210
|
+
<!-- Bad -->
|
|
211
|
+
<input type=text>
|
|
212
|
+
|
|
213
|
+
<!-- Good -->
|
|
214
|
+
<input type="text">
|
|
215
|
+
```
|
|
216
|
+
|
|
217
|
+
---
|
|
218
|
+
|
|
219
|
+
## 14. 디렉티브 단축형 일관성
|
|
220
|
+
|
|
221
|
+
`:`, `@`, `#` 단축형을 항상 사용하거나, 항상 사용하지 않는다. 혼용 금지.
|
|
222
|
+
|
|
223
|
+
```vue
|
|
224
|
+
<!-- Good: 항상 단축형 -->
|
|
225
|
+
<input :value="val" @input="onInput">
|
|
226
|
+
<template #header>...</template>
|
|
227
|
+
|
|
228
|
+
<!-- Good: 항상 전체형 -->
|
|
229
|
+
<input v-bind:value="val" v-on:input="onInput">
|
|
230
|
+
<template v-slot:header>...</template>
|
|
231
|
+
```
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Vue 3 스타일 가이드 - Priority C (Recommended)
|
|
3
|
+
source: https://vuejs.org/style-guide/rules-recommended
|
|
4
|
+
fetched: 2026-05-18
|
|
5
|
+
category: style-guide
|
|
6
|
+
vue_version: 3
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Priority C: Recommended
|
|
10
|
+
|
|
11
|
+
여러 동등한 선택지가 있을 때 일관성을 위해 권장되는 규칙.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. 컴포넌트/인스턴스 옵션 순서 (Options API)
|
|
16
|
+
|
|
17
|
+
1. `name`
|
|
18
|
+
2. `compilerOptions`
|
|
19
|
+
3. `components`, `directives`
|
|
20
|
+
4. `extends`, `mixins`, `provide`/`inject`
|
|
21
|
+
5. `inheritAttrs`, `props`, `emits`, `expose`
|
|
22
|
+
6. `setup`
|
|
23
|
+
7. `data`, `computed`
|
|
24
|
+
8. `watch`, lifecycle hooks (`beforeCreate` → `serverPrefetch` 순서)
|
|
25
|
+
9. `methods`
|
|
26
|
+
10. `template`/`render`
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. 요소 속성 순서
|
|
31
|
+
|
|
32
|
+
1. `is`
|
|
33
|
+
2. `v-for`
|
|
34
|
+
3. `v-if`, `v-else-if`, `v-else`, `v-show`, `v-cloak`
|
|
35
|
+
4. `v-pre`, `v-once`
|
|
36
|
+
5. `id`
|
|
37
|
+
6. `ref`, `key`
|
|
38
|
+
7. `v-model`
|
|
39
|
+
8. 기타 바인딩/속성
|
|
40
|
+
9. `v-on`
|
|
41
|
+
10. `v-html`, `v-text`
|
|
42
|
+
|
|
43
|
+
---
|
|
44
|
+
|
|
45
|
+
## 3. 컴포넌트/인스턴스 옵션 사이 빈 줄
|
|
46
|
+
|
|
47
|
+
여러 줄 속성 사이에는 빈 줄을 추가해 가독성을 높인다.
|
|
48
|
+
|
|
49
|
+
```js
|
|
50
|
+
// Good (Composition API)
|
|
51
|
+
defineProps({
|
|
52
|
+
value: {
|
|
53
|
+
type: String,
|
|
54
|
+
required: true
|
|
55
|
+
},
|
|
56
|
+
|
|
57
|
+
focused: {
|
|
58
|
+
type: Boolean,
|
|
59
|
+
default: false
|
|
60
|
+
},
|
|
61
|
+
|
|
62
|
+
label: String,
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const formattedValue = computed(() => { /* ... */ })
|
|
66
|
+
|
|
67
|
+
const inputClasses = computed(() => { /* ... */ })
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
---
|
|
71
|
+
|
|
72
|
+
## 4. SFC 최상위 요소 순서
|
|
73
|
+
|
|
74
|
+
`<script>`, `<template>`, `<style>` 순서를 프로젝트 전체에서 일관되게 유지한다. `<style>`은 항상 마지막.
|
|
75
|
+
|
|
76
|
+
```vue
|
|
77
|
+
<!-- Option 1: script first -->
|
|
78
|
+
<script>/* ... */</script>
|
|
79
|
+
<template>...</template>
|
|
80
|
+
<style>/* ... */</style>
|
|
81
|
+
|
|
82
|
+
<!-- Option 2: template first -->
|
|
83
|
+
<template>...</template>
|
|
84
|
+
<script>/* ... */</script>
|
|
85
|
+
<style>/* ... */</style>
|
|
86
|
+
```
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
---
|
|
2
|
+
title: Vue 3 스타일 가이드 - Priority D (Use with Caution)
|
|
3
|
+
source: https://vuejs.org/style-guide/rules-use-with-caution
|
|
4
|
+
fetched: 2026-05-18
|
|
5
|
+
category: style-guide
|
|
6
|
+
vue_version: 3
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Priority D: Use with Caution
|
|
10
|
+
|
|
11
|
+
드문 엣지 케이스나 레거시 마이그레이션을 위해 존재하며, 과용 시 유지보수가 어려워진다.
|
|
12
|
+
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
## 1. scoped에서 요소 선택자 지양
|
|
16
|
+
|
|
17
|
+
`scoped`에서 요소 선택자는 class 선택자보다 훨씬 느리다. 반드시 class를 사용한다.
|
|
18
|
+
|
|
19
|
+
```vue
|
|
20
|
+
<!-- Bad: 요소 선택자 (느림) -->
|
|
21
|
+
<style scoped>
|
|
22
|
+
button { background-color: red; }
|
|
23
|
+
</style>
|
|
24
|
+
|
|
25
|
+
<!-- Good: class 선택자 (빠름) -->
|
|
26
|
+
<template>
|
|
27
|
+
<button class="btn btn-close">×</button>
|
|
28
|
+
</template>
|
|
29
|
+
<style scoped>
|
|
30
|
+
.btn-close { background-color: red; }
|
|
31
|
+
</style>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
**이유:** Vue는 `scoped` 사용 시 `button[data-v-f3f3eg9]` 형태로 변환하는데, 이 속성-요소 복합 선택자는 `.btn-close[data-v-f3f3eg9]`보다 현저히 느리다.
|
|
35
|
+
|
|
36
|
+
---
|
|
37
|
+
|
|
38
|
+
## 2. 암묵적 부모-자식 통신 지양
|
|
39
|
+
|
|
40
|
+
`this.$parent`나 prop 직접 변경 대신 props down / events up 패턴을 따른다.
|
|
41
|
+
|
|
42
|
+
```vue
|
|
43
|
+
<!-- Bad: prop 직접 변경 -->
|
|
44
|
+
<script setup>
|
|
45
|
+
const props = defineProps({ todo: { type: Object, required: true } })
|
|
46
|
+
|
|
47
|
+
function renameTodo() {
|
|
48
|
+
props.todo.text = 'renamed by child' // ❌ 부모 상태 직접 변경
|
|
49
|
+
}
|
|
50
|
+
</script>
|
|
51
|
+
|
|
52
|
+
<!-- Good: emit으로 업데이트 요청 -->
|
|
53
|
+
<script setup>
|
|
54
|
+
const props = defineProps({ todo: { type: Object, required: true } })
|
|
55
|
+
const emit = defineEmits(['update:todo'])
|
|
56
|
+
|
|
57
|
+
function renameTodo() {
|
|
58
|
+
emit('update:todo', { ...props.todo, text: 'renamed by parent' }) // ✅
|
|
59
|
+
}
|
|
60
|
+
</script>
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
```vue
|
|
64
|
+
<!-- Bad: this.$parent 사용 -->
|
|
65
|
+
<!-- Good: emit('delete') 사용 -->
|
|
66
|
+
<script setup>
|
|
67
|
+
const emit = defineEmits(['delete'])
|
|
68
|
+
</script>
|
|
69
|
+
<template>
|
|
70
|
+
<button @click="emit('delete')">×</button>
|
|
71
|
+
</template>
|
|
72
|
+
```
|
|
@@ -50,12 +50,13 @@ const SELF_SCORE_PATTERNS = [
|
|
|
50
50
|
function extractDeltas(text) {
|
|
51
51
|
const re = /(\d+(?:\.\d+)?)\s*(?:→|->|–>|~>)\s*(\d+(?:\.\d+)?)/g;
|
|
52
52
|
const out = [];
|
|
53
|
-
let m;
|
|
54
|
-
while (
|
|
53
|
+
let m = re.exec(text);
|
|
54
|
+
while (m !== null) {
|
|
55
55
|
const from = Number(m[1]);
|
|
56
56
|
const to = Number(m[2]);
|
|
57
57
|
if (Number.isFinite(from) && Number.isFinite(to))
|
|
58
58
|
out.push({ from, to });
|
|
59
|
+
m = re.exec(text);
|
|
59
60
|
}
|
|
60
61
|
return out;
|
|
61
62
|
}
|
|
@@ -66,9 +67,10 @@ function findScoreSignals(text, max = 3) {
|
|
|
66
67
|
break;
|
|
67
68
|
// 각 호출마다 lastIndex 초기화를 위해 새 RegExp 생성
|
|
68
69
|
const re = new RegExp(p.source, p.flags);
|
|
69
|
-
let m;
|
|
70
|
-
while (
|
|
70
|
+
let m = re.exec(text);
|
|
71
|
+
while (m !== null && out.length < max) {
|
|
71
72
|
out.push(m[0]);
|
|
73
|
+
m = re.exec(text);
|
|
72
74
|
}
|
|
73
75
|
}
|
|
74
76
|
return out;
|
package/dist/cli.js
CHANGED
|
@@ -186,6 +186,48 @@ const commands = [
|
|
|
186
186
|
console.log(renderResult(result, dryRun));
|
|
187
187
|
},
|
|
188
188
|
},
|
|
189
|
+
{
|
|
190
|
+
name: 'status',
|
|
191
|
+
description: 'Observability dashboard (--watch, --json, --interval N)',
|
|
192
|
+
handler: async (args) => {
|
|
193
|
+
const { runDashboard } = await import('./core/dashboard-cli.js');
|
|
194
|
+
const watch = args.includes('--watch');
|
|
195
|
+
const json = args.includes('--json');
|
|
196
|
+
const intervalIdx = args.indexOf('--interval');
|
|
197
|
+
const intervalSec = intervalIdx !== -1 ? Number(args[intervalIdx + 1]) || 5 : 5;
|
|
198
|
+
await runDashboard({ watch, json, intervalSec });
|
|
199
|
+
},
|
|
200
|
+
},
|
|
201
|
+
{
|
|
202
|
+
name: 'maintenance',
|
|
203
|
+
description: 'Maintenance utilities (--backfill [--phase A|B|all] [--force] [--dry-run])',
|
|
204
|
+
handler: async (args) => {
|
|
205
|
+
if (args.includes('--backfill')) {
|
|
206
|
+
const phaseArg = args[args.indexOf('--phase') + 1];
|
|
207
|
+
const phase = (phaseArg === 'A' || phaseArg === 'B' || phaseArg === 'all') ? phaseArg : 'A';
|
|
208
|
+
const force = args.includes('--force');
|
|
209
|
+
const dryRun = args.includes('--dry-run');
|
|
210
|
+
const { runBackfill } = await import('./core/observability-backfill.js');
|
|
211
|
+
try {
|
|
212
|
+
const result = await runBackfill({ phase, force, dryRun });
|
|
213
|
+
const dryTag = dryRun ? ' [dry-run]' : '';
|
|
214
|
+
console.log(`\n [forgen] Backfill complete${dryTag}:`);
|
|
215
|
+
console.log(` Phase A — matched: ${result.phaseA.matched}, surfaced: ${result.phaseA.surfaced}, acted_on: ${result.phaseA.acted_on}`);
|
|
216
|
+
if (phase !== 'A')
|
|
217
|
+
console.log(` Phase B — acted_on: ${result.phaseB.acted_on}`);
|
|
218
|
+
console.log(` Total: ${result.total} events, ${result.durationMs}ms\n`);
|
|
219
|
+
}
|
|
220
|
+
catch (e) {
|
|
221
|
+
console.error(` [forgen] Backfill 실패: ${e instanceof Error ? e.message : String(e)}`);
|
|
222
|
+
console.error(' --force 플래그로 강행하거나 --dry-run 으로 미리 확인하세요.');
|
|
223
|
+
process.exit(1);
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
console.log('Usage:\n forgen maintenance --backfill [--phase A|B|all] [--force] [--dry-run]');
|
|
228
|
+
}
|
|
229
|
+
},
|
|
230
|
+
},
|
|
189
231
|
{
|
|
190
232
|
name: 'parity',
|
|
191
233
|
description: 'Run host parity checks. Usage: forgen parity codex [--dry-run]',
|
|
@@ -237,10 +279,13 @@ const commands = [
|
|
|
237
279
|
},
|
|
238
280
|
{
|
|
239
281
|
name: 'doctor',
|
|
240
|
-
description: 'Diagnostics (--prune-state to GC stale session files)',
|
|
282
|
+
description: 'Diagnostics (--prune-state to GC stale session files, --repair to auto-fix plugin cache)',
|
|
241
283
|
handler: async (args) => {
|
|
242
284
|
const { runDoctor } = await import('./core/doctor.js');
|
|
243
|
-
await runDoctor({
|
|
285
|
+
await runDoctor({
|
|
286
|
+
pruneState: args.includes('--prune-state'),
|
|
287
|
+
repair: args.includes('--repair'),
|
|
288
|
+
});
|
|
244
289
|
},
|
|
245
290
|
},
|
|
246
291
|
// install --plugin 제거됨 — postinstall이 유일한 설치 경로
|
|
@@ -219,8 +219,12 @@ function validateSolutionFiles(dirBefore) {
|
|
|
219
219
|
function extractText(c) {
|
|
220
220
|
if (typeof c === 'string')
|
|
221
221
|
return c;
|
|
222
|
-
if (Array.isArray(c))
|
|
223
|
-
return c
|
|
222
|
+
if (Array.isArray(c)) {
|
|
223
|
+
return c
|
|
224
|
+
.filter((x) => typeof x === 'object' && x !== null && x.type === 'text')
|
|
225
|
+
.map((x) => (typeof x.text === 'string' ? x.text : ''))
|
|
226
|
+
.join('\n');
|
|
227
|
+
}
|
|
224
228
|
return '';
|
|
225
229
|
}
|
|
226
230
|
/**
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Forgen — Dashboard CLI (P3)
|
|
3
|
+
*
|
|
4
|
+
* `fgx status [--watch] [--json] [--interval N]`
|
|
5
|
+
* ANSI box-drawing 기반 상태 대시보드. TUI 라이브러리 없음.
|
|
6
|
+
*/
|
|
7
|
+
export interface DashboardOptions {
|
|
8
|
+
watch?: boolean;
|
|
9
|
+
json?: boolean;
|
|
10
|
+
intervalSec?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function runDashboard(opts: DashboardOptions): Promise<void>;
|