jutell 0.3.0 → 1.0.0

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.
@@ -5,7 +5,7 @@
5
5
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
6
6
  <meta name="description" content="JuTell by Ju0 개인 베타 로컬 관리자" />
7
7
  <title>JuTell 개인 베타 관리자</title>
8
- <script type="module" crossorigin src="/assets/index-Gxd8X8ii.js"></script>
8
+ <script type="module" crossorigin src="/assets/index-4Pw2ygFb.js"></script>
9
9
  <link rel="stylesheet" crossorigin href="/assets/index-CVml-p-C.css">
10
10
  </head>
11
11
  <body>
@@ -91,6 +91,11 @@ function changedFields(before, after) {
91
91
  if (before.usageMeasurement.localCountersEnabled !== after.usageMeasurement.localCountersEnabled) {
92
92
  changes.push({ field: "usageMeasurement.localCountersEnabled", before: before.usageMeasurement.localCountersEnabled, after: after.usageMeasurement.localCountersEnabled });
93
93
  }
94
+ const beforeVoice = before.voice?.preset ?? "default";
95
+ const afterVoice = after.voice?.preset ?? "default";
96
+ if (beforeVoice !== afterVoice) {
97
+ changes.push({ field: "voice.preset", before: beforeVoice, after: afterVoice });
98
+ }
94
99
  return changes;
95
100
  }
96
101
  var SUPPORTED_VERSION, FEATURE_IDS, PROFILES, DEFAULT_CONFIG, LIMIT_RANGES, isRecord, hasOnlyKeys;
@@ -1,5 +1,6 @@
1
1
  import { promises as fs } from 'node:fs';
2
2
  import path from 'node:path';
3
+ export const VOICE_PRESETS = ['default', 'plain', 'learning', 'jutell'];
3
4
  export const FEATURE_IDS = ['changeSummary', 'userVisibleChanges', 'internalChanges', 'mainFiles', 'explainedDiff', 'glossary', 'validationResults', 'riskAssessment', 'userActions', 'nextActionSuggestions', 'requestClarificationGuide', 'manualEditGuidance', 'requestBuilder'];
4
5
  export const PROFILE_FEATURES = {
5
6
  minimal: { changeSummary: true, userVisibleChanges: true, internalChanges: false, mainFiles: false, explainedDiff: false, glossary: false, validationResults: true, riskAssessment: false, userActions: true, nextActionSuggestions: false, requestClarificationGuide: false, manualEditGuidance: false, requestBuilder: true },
@@ -14,6 +15,7 @@ export const DEFAULT_CONFIG = {
14
15
  limits: { maxMainFiles: 5, maxGlossaryTerms: 3, compactReportMaxSentences: 12 },
15
16
  mcp: { enabled: false },
16
17
  usageMeasurement: { localCountersEnabled: false },
18
+ voice: { preset: 'default' },
17
19
  };
18
20
  const isRecord = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
19
21
  const hasOnlyKeys = (value, keys) => Object.keys(value).every((key) => keys.includes(key));
@@ -35,7 +37,13 @@ export function normalizeConfig(value) {
35
37
  return structuredClone(DEFAULT_CONFIG);
36
38
  if ('usageMeasurement' in value && (!isRecord(value.usageMeasurement) || !hasOnlyKeys(value.usageMeasurement, ['localCountersEnabled']) || typeof value.usageMeasurement.localCountersEnabled !== 'boolean'))
37
39
  return structuredClone(DEFAULT_CONFIG);
40
+ if ('voice' in value && (!isRecord(value.voice) || !hasOnlyKeys(value.voice, ['preset']) || (value.voice.preset !== undefined && !VOICE_PRESETS.includes(String(value.voice.preset))))) {
41
+ return structuredClone(DEFAULT_CONFIG);
42
+ }
38
43
  const features = Object.fromEntries(FEATURE_IDS.map((id) => [id, typeof featuresInput[id] === 'boolean' ? featuresInput[id] : PROFILE_FEATURES[value.profile][id]]));
44
+ const voicePreset = isRecord(value.voice) && typeof value.voice.preset === 'string' && VOICE_PRESETS.includes(value.voice.preset)
45
+ ? value.voice.preset
46
+ : DEFAULT_CONFIG.voice.preset;
39
47
  return {
40
48
  version: 1,
41
49
  profile: value.profile,
@@ -43,6 +51,7 @@ export function normalizeConfig(value) {
43
51
  limits: { ...value.limits },
44
52
  mcp: isRecord(value.mcp) ? { enabled: value.mcp.enabled } : { ...DEFAULT_CONFIG.mcp },
45
53
  usageMeasurement: isRecord(value.usageMeasurement) ? { localCountersEnabled: value.usageMeasurement.localCountersEnabled } : { ...DEFAULT_CONFIG.usageMeasurement },
54
+ voice: { preset: voicePreset },
46
55
  };
47
56
  }
48
57
  async function readText(file) {
@@ -5,7 +5,7 @@ import { activeFeatures, beginnerReportRules, bridgeStatus, reportPreferences, s
5
5
  import { recordToolCall } from './tools/usage-counters.js';
6
6
  const server = new McpServer({
7
7
  name: 'JuTell',
8
- version: '0.3.0',
8
+ version: '1.0.0',
9
9
  }, {
10
10
  instructions: 'JuTell by Ju0 is a local read-only report helper. Read only project configuration and approved report rules. Never access project code, Git diff, prompts, AI answers, secrets, or external networks. Skill mode remains available if this MCP server is disabled or unavailable. When both jutell and beginner_bridge servers are visible, prefer the canonical jutell server; use beginner_bridge only for compatibility. For owner-facing reports, apply the JuTell reporting guidance before composing the final answer.',
11
11
  });
@@ -13,6 +13,7 @@ export function bridgeStatus(context) {
13
13
  skillFileExists: context.skillExists,
14
14
  agentsFileExists: context.agentsExists,
15
15
  profile: context.config.profile,
16
+ voicePreset: context.config.voice.preset,
16
17
  activeFeatureCount: Object.values(context.config.features).filter(Boolean).length,
17
18
  configVersion: context.config.version,
18
19
  skillVersion: parseSkillVersion(context.skillText),
@@ -28,6 +29,7 @@ export function reportPreferences(config) {
28
29
  return {
29
30
  profile: config.profile,
30
31
  profileName: profileLabel[config.profile],
32
+ voicePreset: config.voice.preset,
31
33
  mainFilesLevel: config.limits.maxMainFiles <= 3 ? '최소' : config.limits.maxMainFiles <= 5 ? '보통' : '많이',
32
34
  glossaryLevel: config.limits.maxGlossaryTerms === 0 ? '사용 안 함' : config.limits.maxGlossaryTerms <= 1 ? '조금' : config.limits.maxGlossaryTerms <= 3 ? '보통' : '많이',
33
35
  reportLengthLevel: reportLength(config.limits.compactReportMaxSentences),
@@ -35,26 +37,76 @@ export function reportPreferences(config) {
35
37
  };
36
38
  }
37
39
  export const EXPLAINED_DIFF_SECTIONS = ['무엇을 바꿨나요?', '왜 바꿨나요?', '어디를 바꿨나요?', '실제 중요한 변경', '내가 직접 다듬고 싶다면?'];
40
+ const readableCodeRule = {
41
+ when: '중요한 코드 블록이 이미 작업 과정에서 확인되었고, 사용자 이해에 실제 도움이 될 때만 1~2개까지 보여준다.',
42
+ maxSnippets: 2,
43
+ absoluteMaxSnippets: 2,
44
+ form: ['[중요한 코드]', '짧은 코드 블록', '[쉽게 보면]', '[영향]'],
45
+ reuseOnly: '이미 읽거나 비교한 코드만 사용한다. 이 섹션만을 위해 파일을 다시 읽거나 git diff를 다시 실행하지 않는다.',
46
+ omitWhen: [
47
+ '이미 확인된 코드 근거가 없다',
48
+ '긴 raw diff를 반복하는 형태다',
49
+ '줄별 프로그래밍 문법 강의가 된다',
50
+ ],
51
+ cautionAreas: '인증·권한·결제·데이터베이스처럼 민감한 영역은 코드 일부를 보여주더라도 주의가 필요하다고 표시한다.',
52
+ };
38
53
  const explainedDiffRule = {
39
54
  when: '의미 있는 변경(기능 동작·화면 변화·데이터 처리 변화)을 보고할 때',
40
55
  sections: EXPLAINED_DIFF_SECTIONS,
41
56
  groupingRule: '관련된 파일과 변경은 기능 단위로 묶어 설명하고 전체 Diff 원문을 반복하지 않습니다.',
57
+ readableCodeRule,
42
58
  noEvidenceRules: {
43
59
  why: '변경 이유의 근거가 없으면 추측하지 않고 "변경 이유는 Agent 결과에서 확인되지 않았습니다."로 표시합니다.',
44
60
  customization: '문구·색상·여백·이동 경로처럼 직접 다듬을 수 있는 위치의 코드 근거가 없으면 "내가 직접 다듬고 싶다면?" 섹션을 만들지 않습니다.',
45
61
  riskyArea: '인증·권한·결제·데이터베이스처럼 위험한 영역은 간단한 화면 다듬기 대상으로 제시하지 않고 주의가 필요하다고 표시합니다.',
46
62
  },
47
63
  };
64
+ const handoffRule = {
65
+ when: '사용자가 다음 AI에게 작업을 넘기거나, 세션을 마무리하며 이어서 할 일을 남겨달라고 할 때',
66
+ title: '다음 AI에게 전달하기',
67
+ sections: ['지금 하던 일', '방금 끝난 것', '확인된 것', '아직 확인하지 못한 것', '다음에 해줬으면 하는 것', '먼저 보면 좋은 파일'],
68
+ optionalSections: ['건드리면 안 되는 것', '사용자 결정이 필요한 것'],
69
+ evidenceRule: '현재 작업/세션에서 이미 아는 근거만 사용한다. 저장소를 다시 읽거나 테스트를 다시 돌리거나 자동으로 다른 Agent에 보내지 않는다.',
70
+ format: 'Markdown 복사 가능한 텍스트만 사용한다. HTML·클라우드·자동 오케스트레이션을 만들지 않는다.',
71
+ templateHint: 'Request Builder의 NEXT_AGENT_HANDOFF 템플릿 또는 세션 SESSION_SUMMARY 내용을 재사용해도 된다.',
72
+ };
73
+ const voiceRules = {
74
+ default: {
75
+ preset: 'default',
76
+ style: '일반적이고 명확한 비개발자용 설명',
77
+ guidance: '친절하고 분명한 쉬운 한국어로 설명한다. 사실·예상·미확인·위험·사용자 행동을 구분한다.',
78
+ },
79
+ plain: {
80
+ preset: 'plain',
81
+ style: '짧고 직접적인 평이한 문장',
82
+ guidance: '문장을 짧게 유지하고 군더더기 없이 핵심만 말한다. 근거·위험·검증 진실을 줄이지 않는다.',
83
+ },
84
+ learning: {
85
+ preset: 'learning',
86
+ style: '필요한 용어를 조금 더 풀어 설명',
87
+ guidance: '필수 용어가 나오면 짧은 쉬운 설명을 덧붙인다. 용어 설명을 늘려도 근거와 확인 상태는 그대로 둔다.',
88
+ },
89
+ jutell: {
90
+ preset: 'jutell',
91
+ style: '친절하고 자연스럽고 간결한 JuTell Style',
92
+ guidance: '딱딱한 문장을 현실적인 말로 풀고 핵심부터 말한다. 사실·검증·위험을 줄이거나 과장하지 않는다.',
93
+ },
94
+ };
95
+ export function voiceRule(config) {
96
+ return voiceRules[config.voice.preset] ?? voiceRules.default;
97
+ }
48
98
  export function beginnerReportRules(config) {
49
99
  const active = Object.entries(config.features).filter(([, enabled]) => enabled).map(([id]) => FEATURE_CATALOG[id].label);
50
100
  return {
51
101
  language: '비개발자가 이해할 수 있는 쉬운 한국어',
52
102
  activeReportSections: active,
53
103
  limits: config.limits,
104
+ voiceRule: voiceRule(config),
54
105
  evidenceRule: '실제로 확인한 사실, 코드만 보고 예상한 내용, 확인하지 못한 내용을 구분합니다.',
55
106
  statusRule: '검증 결과와 보고서 상태를 일치시킵니다.',
56
107
  diffRule: '코드 또는 Diff를 사용자에게 보여줄 경우, 바로 뒤에 무엇을 수정했고 사용자에게 어떤 영향이 있는지 기능 단위로 설명합니다.',
57
108
  ...(config.features.explainedDiff ? { explainedDiffRule } : {}),
109
+ ...(config.features.requestBuilder ? { handoffRule } : {}),
58
110
  safetyRequirements: SAFETY_REQUIREMENTS,
59
111
  notCollected: ['프로젝트 코드', 'Git diff', 'Prompt', 'AI 답변 원문', '파일 경로', '비밀정보'],
60
112
  };
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: beginner-bridge
3
- jutellSkillVersion: "0.3.0"
3
+ jutellSkillVersion: "1.0.0"
4
4
  schemaVersion: 1
5
5
  description: JuTell by Ju0 creates concise, evidence-based work reports for non-developers, separating observed facts, code-based expectations, verification results, risks, and user actions. The legacy Skill ID is retained for compatibility.
6
6
  ---
@@ -41,9 +41,10 @@ Codex 작업 결과를 비개발자가 이해할 수 있는 하나의 보고서
41
41
  2. JSON, `version`, Profile, Feature ID, boolean 값과 limits를 검증한다.
42
42
  3. 선택한 Profile의 기본값을 적용한다. 파일이 없으면 `balanced`를 사용한다.
43
43
  4. 명시적으로 적힌 `features`와 `limits`를 Profile 기본값보다 우선 적용한다.
44
- 5. 안전상 강제 보고 항목과 사용자가 현재 요청에서 요구한 형식을 적용한다.
45
- 6. 이번 보고서에서 사용할 최종 활성 Feature와 limits를 확정한다.
46
- 7. 활성 Feature만 사용해 보고서를 작성하되, 강제 보고 항목은 생략하지 않는다.
44
+ 5. `voice.preset`이 있으면 보고 문체에만 적용한다. 지원 값: `default`, `plain`, `learning`, `jutell`. 없거나 잘못된 값이면 `default`로 진행한다. 말투를 바꿔도 사실·검증·위험·사용자 행동은 줄이거나 과장하지 않는다.
45
+ 6. 안전상 강제 보고 항목과 사용자가 현재 요청에서 요구한 형식을 적용한다.
46
+ 7. 이번 보고서에서 사용할 최종 활성 Feature와 limits를 확정한다.
47
+ 8. 활성 Feature만 사용해 보고서를 작성하되, 강제 보고 항목은 생략하지 않는다.
47
48
 
48
49
  설정 오류가 있으면 전체 설정을 추측해 고치지 않고 `balanced`로 진행한다. 오류가 있는 경우에만 설정 문제를 짧게 알리며 설정 파일 전체는 출력하지 않는다.
49
50
 
@@ -78,14 +79,15 @@ JuTell MCP가 보이면 canonical `jutell` 서버를 사용한다. `jutell`과 `
78
79
  * 보고서 상태: 확인 완료, 추가 확인 필요, 일부 확인, 작업 보류, 범위 밖
79
80
  11. 위험도는 변경 영향도를 기준으로 판단한다. 검증 도구가 없다는 이유만으로 위험도를 판정 불가로 만들지 않는다. 여러 조건이 겹치면 가장 높은 위험도를 적용한다.
80
81
  12. 필요한 용어만 처음 등장할 때 설명한다. 기술 전용 용어는 해당 기술이 사용되는 것을 확인한 경우에만 설명한다. 기본 사전에 없는 용어는 추측하지 않는다.
81
- 13. 활성 Feature와 `references/report-format.md`에 따라 하나의 비개발자용 최종 보고를 작성한다. `explainedDiff`가 활성이면 의미 있는 변경에 같은 근거로 설명형 변경 요약을 덧붙인다.
82
- 14. `nextActionSuggestions`가 활성일 때 보고서 끝에 다음 행동 제안을 **최대 3개**만 추가한다. 다음 경우에만 제안한다.
82
+ 13. 활성 Feature와 `references/report-format.md`에 따라 하나의 비개발자용 최종 보고를 작성한다. `explainedDiff`가 활성이면 의미 있는 변경에 같은 근거로 설명형 변경 요약을 덧붙인다. 중요한 코드 블록이 이미 작업 과정에서 확인되었고 사용자 이해에 실제 도움이 될 때만 1~2개까지 보여준다. 이 섹션만을 위해 파일을 다시 읽거나 git diff를 다시 실행하지 않는다. `explainedDiff`가 꺼져 있으면 Readable Code도 생략한다(사용자가 코드 설명을 명시한 경우만 예외).
83
+ 14. `requestBuilder`가 활성이고 사용자가 다음 AI에게 넘기기·세션 마무리·이어서 할 일 정리를 요청하면, 현재 작업에서 이미 아는 근거만으로 `다음 AI에게 전달하기` 블록을 만든다. Request Builder의 `NEXT_AGENT_HANDOFF` 템플릿 또는 세션 `SESSION_SUMMARY`를 재사용해도 된다. 저장소 재탐색, 테스트 재실행, 자동 전송, HTML/클라우드 산출물은 만들지 않는다.
84
+ 15. `nextActionSuggestions`가 활성일 때 보고서 끝에 다음 행동 제안을 **최대 3개**만 추가한다. 다음 경우에만 제안한다.
83
85
  * 사용자가 직접 확인해야 할 항목이 남은 경우
84
86
  * 검증되지 않아 보류된 항목이 있는 경우
85
87
  * 설정이 필요한 경우
86
88
  * 데이터 손실·보안 위험이 확인된 경우
87
89
  제안은 보고서에서 이미 확인된 항목 중에서만 고르고, 추측이나 새 작업을 제안하지 않는다. 해당하지 않으면 생략한다.
88
- 15. 제출 전 다음을 점검한다.
90
+ 16. 제출 전 다음을 점검한다.
89
91
  * 화면 변화와 내부 변화를 분리했는가
90
92
  * 예상과 실제 확인을 구분했는가
91
93
  * 근거가 중요한 주장과 일치하는가
@@ -77,14 +77,39 @@
77
77
  * 인증, 권한, 결제, 데이터베이스 처리 같은 영역은 간단한 다듬기 대상으로 제시하지 않는다. 대신 "이 부분은 간단한 화면 수정 대상이 아니라 주의가 필요한 변경입니다."처럼 표시한다.
78
78
  * TypeScript 문법 강의, React 줄별 해설처럼 구현 세부를 가르치지 않는다.
79
79
 
80
- ## 5. 묶기와 길이
80
+ ## 5. 중요한 코드 (Readable Code)
81
+
82
+ 중요한 코드 블록이 이미 작업 과정에서 확인되었고, 사용자 이해에 실제 도움이 될 때만 1~2개까지 보여준다. 한 보고에서 2개를 넘지 않는다.
83
+
84
+ 형식 예시:
85
+
86
+ - `[중요한 코드]` 뒤에 짧은 코드 블록 1개
87
+ - `[쉽게 보면]`에 비개발자용 한두 문장
88
+ - `[영향]`에 사용자에게 달라지는 점 한두 문장
89
+
90
+ 예시 코드: `if (!query.trim()) return;`
91
+ 쉽게 보면: 빈칸이나 공백만 입력한 상태에서는 검색이 실행되지 않도록 막는 부분입니다.
92
+ 영향: 사용자가 공백만 입력했을 때 불필요한 검색 요청이 발생하지 않습니다.
93
+
94
+ 규칙:
95
+
96
+ * 이미 읽거나 비교한 코드만 사용한다.
97
+ * 이 섹션만을 위해 파일을 다시 읽지 않는다.
98
+ * 이 섹션만을 위해 git diff를 다시 실행하지 않는다.
99
+ * 코드 근거가 없으면 이 섹션을 만들지 않는다.
100
+ * 긴 raw diff를 반복하지 않는다.
101
+ * 줄별 프로그래밍 문법 강의를 하지 않는다.
102
+ * 인증·권한·결제·데이터베이스처럼 민감한 영역은 보여주더라도 주의가 필요하다고 표시한다.
103
+ * `explainedDiff`가 OFF이면 Readable Code도 생략한다. 사용자가 코드 설명을 명시적으로 요청한 경우만 예외다.
104
+
105
+ ## 6. 묶기와 길이
81
106
 
82
107
  * 관련된 변경은 기능 단위로 묶는다. 파일마다 다섯 항목을 반복하지 않는다.
83
108
  * 전체 Diff 원문을 다시 출력하지 않는다. 중요한 변경 블록만 요약한다.
84
109
  * [실제 중요한 변경]은 확인된 핵심 동작 위주로 작성하고, 같은 내용을 다른 보고 항목에서 반복하지 않는다.
85
110
  * 기존 보고서 길이 규칙(`compactReportMaxSentences`, 작업 규모별 길이)이 우선한다. 설명을 붙인다고 길이 제한을 무시하지 않는다.
86
111
 
87
- ## 6. 예시
112
+ ## 7. 예시
88
113
 
89
114
  합성 예시이며 실제 프로젝트가 아니다.
90
115
 
@@ -101,6 +126,15 @@
101
126
  [실제 중요한 변경]
102
127
  - 검색어가 비어 있으면 검색 버튼이 실행되지 않음
103
128
  - 검색어를 다시 입력하면 버튼이 원래대로 활성화됨
129
+
130
+ [중요한 코드]
131
+ if (!query.trim()) return;
132
+
133
+ [쉽게 보면]
134
+ 빈칸이나 공백만 입력한 상태에서는 검색이 실행되지 않도록 막는 부분입니다.
135
+
136
+ [영향]
137
+ 사용자가 공백만 입력했을 때 불필요한 검색 요청이 발생하지 않습니다.
104
138
  ```
105
139
 
106
140
  코드에서 버튼 문구 위치를 직접 확인했다면 마지막에 다음을 추가한다.
@@ -112,6 +146,6 @@
112
146
 
113
147
  변경 이유를 확인할 근거가 없었다면 `[왜 바꿨나요?]`에는 "변경 이유는 Agent 결과에서 확인되지 않았습니다."라고만 쓴다.
114
148
 
115
- ## 7. 용어 설명
149
+ ## 8. 용어 설명
116
150
 
117
151
  설명에 필요한 개발 용어는 `glossary` Feature와 `glossary-ko.md` 규칙을 그대로 사용한다. 이 형식을 위해 별도의 용어 체계를 만들지 않는다.
@@ -142,10 +142,37 @@
142
142
 
143
143
  * 근거가 없는 변경 이유는 추측하지 않고 "변경 이유는 Agent 결과에서 확인되지 않았습니다."로 표시한다.
144
144
  * 문구·색상·경로처럼 직접 다듬을 수 있는 위치의 코드 근거가 있을 때만 "내가 직접 다듬고 싶다면?"을 덧붙인다.
145
+ * 중요한 코드 블록이 이미 작업 과정에서 확인되었고 사용자 이해에 실제 도움이 될 때만 1~2개까지 `[중요한 코드]` / `[쉽게 보면]` / `[영향]` 형식으로 보여준다. 이 섹션만을 위해 파일을 다시 읽거나 git diff를 다시 실행하지 않는다.
146
+ * `explainedDiff`가 OFF이면 Readable Code도 생략한다. 사용자가 코드 설명을 명시한 경우만 예외다.
145
147
  * 단순 작업에는 이 형식을 강제하지 않고 기존 6개 항목을 유지한다.
146
148
 
147
149
  형식과 예시는 `references/explained-diff-format.md`를 따른다.
148
150
 
151
+ ## 6.8 다음 AI에게 전달하기
152
+
153
+ `requestBuilder`가 활성이고 사용자가 다음 AI에게 넘기기·세션 마무리·이어서 할 일 정리를 요청하면, 현재 작업에서 이미 아는 근거만으로 아래 형식을 만든다.
154
+
155
+ * 지금 하던 일
156
+ * 방금 끝난 것
157
+ * 확인된 것
158
+ * 아직 확인하지 못한 것
159
+ * 다음에 해줬으면 하는 것
160
+ * 먼저 보면 좋은 파일
161
+ * (해당할 때만) 건드리면 안 되는 것 / 사용자 결정이 필요한 것
162
+
163
+ Request Builder의 `NEXT_AGENT_HANDOFF` 템플릿 또는 세션 `SESSION_SUMMARY`를 재사용해도 된다. 저장소 재탐색, 테스트 재실행, 자동 전송, HTML/클라우드 산출물은 만들지 않는다.
164
+
165
+ ## 6.9 말투 프리셋 (`voice.preset`)
166
+
167
+ `.jutell.json`의 `voice.preset`이 있으면 보고 문체에만 적용한다. MCP `voiceRule`이 보이면 그것을 우선한다.
168
+
169
+ * `default` — 일반적이고 명확한 비개발자용 설명
170
+ * `plain` — 짧고 직접적인 평이한 문장
171
+ * `learning` — 필요한 용어를 조금 더 풀어 설명
172
+ * `jutell` — 친절하고 자연스럽고 간결한 JuTell Style
173
+
174
+ 말투를 바꿔도 사실·검증·위험·사용자 행동은 줄이거나 과장하지 않는다. 스타일 학습·임베딩·자동 말투 추론은 하지 않는다.
175
+
149
176
  ## 7. 금지 표현
150
177
 
151
178
  근거가 없으면 다음 표현을 사용하지 않는다.
@@ -1,106 +1,91 @@
1
- # 다음 Agent에게 작업 넘기기
2
-
3
- > 이 템플릿은 지금까지의 작업 내용을 다음 AI Agent(또는 새 대화)에 전달할 때 사용합니다.
4
- > 작성법: `[ ]`에 체크하고, 빈칸은 쉬운 말로 채웁니다. 모르는 항목은 비워 두세요.
5
-
6
- ## 1. 지금 만드는 것
7
-
8
- 프로젝트와 지금 만들고 있는 화면이나 기능을 한두 문장으로 설명해 주세요.
9
-
10
- ```
11
- (예: 메모를 저장하고 목록으로 보여주는 앱의 검색 기능)
12
- ```
13
-
14
- - [ ] 잘 모르겠음
15
- - [ ] AI가 먼저 확인 후 제안
16
-
17
- ## 2. 지금까지의 작업 요약
18
-
19
- 이미 무엇을 했는지, 지금 어디까지 되어 있는지 적어 주세요.
20
-
21
- ```
22
- (예: 메모 저장과 목록 표시까지 완료했고, 검색 기능을 만들던 중이다)
23
- ```
24
-
25
- - [ ] 잘 모르겠음 (AI가 현재 상태를 확인해 정리해 주세요)
26
- - [ ] AI가 먼저 확인 후 제안
27
-
28
- ## 3. 지금 문제가 되는 것
29
-
30
- 지금 막혀 있거나 잘 안 되는 것을 적어 주세요.
31
-
32
- ```
33
- (예: 검색 결과가 최신 메모부터 나오지 않는다)
34
- ```
35
-
36
- - [ ] 정확히 무엇이 문제인지 모르겠음
37
- - [ ] 요청하지 않음 (막힌 것 없음)
38
-
39
- ## 4. 다음에 할 일
40
-
41
- 다음에 이어서 해야 할 일을 순서대로 적어 주세요. 없으면 비워 두세요.
42
-
43
- ```
44
- 1. (예: 검색 결과 정렬을 수정)
45
- 2. (예: 검색 기록 화면 추가)
46
- ```
47
-
48
- - [ ] 요청하지 않음 (아직 정하지 못함)
49
- - [ ] AI가 먼저 확인 후 제안
50
-
51
- ## 5. 반드시 유지할 부분
52
-
53
- 이전 작업에서 지켜야 하는 방식이나 지우면 안 되는 것을 적어 주세요.
54
-
55
- ```
56
- (예: 저장 방식과 파일 위치는 그대로 유지)
57
- ```
58
-
59
- - [ ] 현재 상태 유지
60
- - [ ] 요청하지 않음
61
-
62
- ## 6. 바꾸면 안 되는 부분
63
-
64
- 다음 작업에서 건드리면 안 되는 것을 적어 주세요.
65
-
66
- ```
67
- (예: 결제 관련 코드는 건드리지 않는다)
68
- ```
69
-
70
- - [ ] 요청하지 않음
71
-
72
- ## 7. 사용자가 직접 확인할 부분
73
-
74
- 다음 작업 후 사용자가 직접 눌러 보거나 확인해야 하는 것을 적어 주세요.
75
-
76
- ```
77
- (예: 검색 결과가 실제 저장된 메모와 맞는지)
78
- ```
79
-
80
- - [ ] 요청하지 않음
81
-
82
- ## 8. AI가 먼저 확인할 질문
83
-
84
- - [ ] AI가 먼저 현재 프로젝트 상태를 확인한 뒤 "지금 상황 요약"을 알려주세요.
85
- - [ ] 이전 작업에서 남은 미완료 항목을 확인해 목록으로 보여주세요.
86
- - [ ] 전달받은 내용 중 빠진 것이 있는지 질문해 주세요.
87
- - [ ] AI가 먼저 확인 후 제안 (위 항목 대신 AI 판단에 맡김)
88
-
89
- ## 9. 최종 요청문
90
-
91
- 아래를 복사해서 다음 AI Agent에 붙여넣으세요. `( )` 자리에 위 내용을 합쳐서 적어도 됩니다.
92
-
93
- ```text
94
- 이 프로젝트의 작업을 이어서 맡아주세요.
95
-
96
- [프로젝트] (1번 항목 내용)
97
- [지금까지 한 것] (2번 항목 내용)
98
- [지금 문제] (3번 항목 내용, 없으면 "없음")
99
- [다음에 할 일] (4번 항목 내용, 없으면 "없음")
100
- [유지할 부분] (5번 항목, 없으면 "없음")
101
- [건드리면 안 되는 부분] (6번 항목, 없으면 "없음")
102
- [사용자 확인] (7번 항목, 없으면 "없음")
103
-
104
- 먼저 현재 상태를 확인하고 "지금 상황 요약"부터 알려주세요.
105
- 확인하지 못한 내용은 추측하지 말고 질문해주세요.
106
- ```
1
+ # 다음 AI에게 전달하기
2
+
3
+ > 지금까지의 작업을 다음 AI Agent(또는 새 대화)에 넘길 때 쓰는 가벼운 전달문입니다.
4
+ > 현재 작업/세션에서 **이미 아는 근거만** 채웁니다. 저장소를 다시 읽거나, 테스트를 다시 돌리거나, 자동으로 다른 Agent에 보내지 않습니다.
5
+ > 모르는 항목은 비워 두거나 "확인하지 못함"으로 적습니다. Markdown 텍스트만 복사해서 사용합니다.
6
+
7
+ ## 지금 하던 일
8
+
9
+ ```
10
+ (예: 메모 앱 검색에서 빈 입력 검색을 막는 작업)
11
+ ```
12
+
13
+ ## 방금 끝난 것
14
+
15
+ ```
16
+ (예: 빈 검색어 가드와 관련 테스트를 반영함)
17
+ ```
18
+
19
+ ## 확인된 것
20
+
21
+ ```
22
+ (예: 단위 테스트 통과. 검색어가 공백이면 요청이 나가지 않음)
23
+ ```
24
+
25
+ ## 아직 확인하지 못한 것
26
+
27
+ ```
28
+ (예: 실제 브라우저에서 검색 버튼 동작은 직접 확인하지 않음)
29
+ ```
30
+
31
+ ## 다음에 해줬으면 하는 것
32
+
33
+ ```
34
+ 1. (예: 검색 결과 정렬을 최신순으로 맞추기)
35
+ 2. (예: 빈 입력 UI 안내 문구 추가)
36
+ ```
37
+
38
+ ## 먼저 보면 좋은 파일
39
+
40
+ ```
41
+ - (예: src/features/search/SearchBar.tsx)
42
+ - (예: src/features/search/SearchBar.test.tsx)
43
+ ```
44
+
45
+ ## 건드리면 안 되는 것 (해당할 때만)
46
+
47
+ ```
48
+ (예: 결제·인증 관련 코드는 건드리지 않는다)
49
+ ```
50
+
51
+ ## 사용자 결정이 필요한 것 (해당할 때만)
52
+
53
+ ```
54
+ (예: 빈 검색 안내 문구를 어떤 말로 할지)
55
+ ```
56
+
57
+ ---
58
+
59
+ ## 복사해서 다음 AI에게 붙여넣기
60
+
61
+ 아래를 복사한 뒤 `( )`를 위 내용으로 채우세요.
62
+
63
+ ```text
64
+ 다음 AI에게 전달하기
65
+
66
+ 지금 하던 일
67
+ ( )
68
+
69
+ 방금 끝난 것
70
+ ( )
71
+
72
+ 확인된 것
73
+ ( )
74
+
75
+ 아직 확인하지 못한 것
76
+ ( )
77
+
78
+ 다음에 해줬으면 하는 것
79
+ ( )
80
+
81
+ 먼저 보면 좋은 파일
82
+ ( )
83
+
84
+ 건드리면 안 되는 것
85
+ (없으면 "없음")
86
+
87
+ 사용자 결정이 필요한 것
88
+ (없으면 "없음")
89
+
90
+ 이미 확인된 근거만 사용하고, 확인하지 못한 내용은 추측하지 말고 질문해주세요.
91
+ ```
@@ -1,5 +1,5 @@
1
1
  {
2
- "cli": "0.3.0",
2
+ "cli": "1.0.0",
3
3
  "skill": "확인 필요",
4
4
  "mcp": "0.1.0",
5
5
  "admin": "0.1.0"
@@ -24,6 +24,7 @@ const fallbackConfig = {
24
24
  limits: { maxMainFiles: 5, maxGlossaryTerms: 3, compactReportMaxSentences: 12 },
25
25
  mcp: { enabled: false },
26
26
  usageMeasurement: { localCountersEnabled: false },
27
+ voice: { preset: 'default' },
27
28
  };
28
29
  export async function exists(file) {
29
30
  try {
@@ -94,6 +95,10 @@ export function normalizeConfig(value) {
94
95
  const inputMcp = input.mcp && typeof input.mcp === 'object' && !Array.isArray(input.mcp) ? input.mcp : {};
95
96
  const inputUsageMeasurement = input.usageMeasurement && typeof input.usageMeasurement === 'object' && !Array.isArray(input.usageMeasurement) ? input.usageMeasurement : {};
96
97
  const inputVoice = input.voice && typeof input.voice === 'object' && !Array.isArray(input.voice) ? input.voice : {};
98
+ const voicePresets = ['default', 'plain', 'learning', 'jutell'];
99
+ const voicePreset = typeof inputVoice.preset === 'string' && voicePresets.includes(inputVoice.preset)
100
+ ? inputVoice.preset
101
+ : 'default';
97
102
  return {
98
103
  version: 1,
99
104
  profile,
@@ -101,7 +106,7 @@ export function normalizeConfig(value) {
101
106
  limits: { maxMainFiles: numberOr('maxMainFiles', 5), maxGlossaryTerms: numberOr('maxGlossaryTerms', 3), compactReportMaxSentences: numberOr('compactReportMaxSentences', 12) },
102
107
  mcp: { enabled: inputMcp.enabled === true },
103
108
  usageMeasurement: { localCountersEnabled: inputUsageMeasurement.localCountersEnabled === true },
104
- ...(typeof inputVoice.preset === 'string' ? { voice: { preset: inputVoice.preset } } : {}),
109
+ voice: { preset: voicePreset },
105
110
  };
106
111
  }
107
112
  export async function readBridgeConfig(paths) {
@@ -140,7 +140,7 @@ export function parseOptions(args) {
140
140
  }
141
141
  export function scopeLabel(scope) { return scope === 'global' ? '사용자 전역' : '현재 프로젝트'; }
142
142
  export function printHelp(io) {
143
- io.write(`JuTell CLI 0.3.0
143
+ io.write(`JuTell CLI 1.0.0
144
144
 
145
145
  시작할 때는 jutell만 입력하면 됩니다.
146
146
  처음 연결하면 안내에 따라 AI Agent와 보고 방식을 고르고
@@ -182,6 +182,6 @@ JuTell은 AI Agent를 대신 실행하지 않고 연결과 보고만 도와줍
182
182
 
183
183
  이전 별칭: beginner-bridge
184
184
 
185
- 실제 배포 전에는 로컬 패키지 검증만 지원합니다. 업데이트는 다음 명령을 사용하세요.
186
- npm update -g jutell`);
185
+ 공개 설치: npm install -g jutell
186
+ 업데이트: npm update -g jutell`);
187
187
  }
@@ -6,7 +6,7 @@ const INITIALIZE = JSON.stringify({
6
6
  jsonrpc: '2.0',
7
7
  id: 1,
8
8
  method: 'initialize',
9
- params: { protocolVersion: PROTOCOL_VERSION, capabilities: {}, clientInfo: { name: 'jutell-doctor', version: '0.3.0' } },
9
+ params: { protocolVersion: PROTOCOL_VERSION, capabilities: {}, clientInfo: { name: 'jutell-doctor', version: '1.0.0' } },
10
10
  });
11
11
  const INITIALIZED = JSON.stringify({ jsonrpc: '2.0', method: 'notifications/initialized' });
12
12
  const TOOLS_LIST = JSON.stringify({ jsonrpc: '2.0', id: 2, method: 'tools/list', params: {} });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jutell",
3
- "version": "0.3.0",
3
+ "version": "1.0.0",
4
4
  "description": "JuTell by Ju0 — non-developer harness for AI coding agents (Skill, MCP, local dashboard)",
5
5
  "license": "MIT",
6
6
  "repository": {