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.
- package/README.md +10 -4
- package/assets/local-admin/assets/{index-Gxd8X8ii.js → index-4Pw2ygFb.js} +14 -14
- package/assets/local-admin/index.html +1 -1
- package/assets/local-admin-server.js +5 -0
- package/assets/mcp-server/config/bridge-config.js +9 -0
- package/assets/mcp-server/index.js +1 -1
- package/assets/mcp-server/tools/bridge-tools.js +52 -0
- package/assets/skill/SKILL.md +9 -7
- package/assets/skill/references/explained-diff-format.md +37 -3
- package/assets/skill/references/report-format.md +27 -0
- package/assets/templates/request-builder/NEXT_AGENT_HANDOFF.md +91 -106
- package/assets/version.json +1 -1
- package/dist/config/managed.js +6 -1
- package/dist/output/format.js +3 -3
- package/dist/process/mcpProbe.js +1 -1
- package/package.json +1 -1
|
@@ -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-
|
|
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.
|
|
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
|
};
|
package/assets/skill/SKILL.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: beginner-bridge
|
|
3
|
-
jutellSkillVersion: "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.
|
|
46
|
-
7.
|
|
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. `
|
|
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
|
-
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
-
# 다음
|
|
2
|
-
|
|
3
|
-
>
|
|
4
|
-
>
|
|
5
|
-
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
```
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
##
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
```
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
다음
|
|
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
|
-
|
|
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
|
+
```
|
package/assets/version.json
CHANGED
package/dist/config/managed.js
CHANGED
|
@@ -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
|
-
|
|
109
|
+
voice: { preset: voicePreset },
|
|
105
110
|
};
|
|
106
111
|
}
|
|
107
112
|
export async function readBridgeConfig(paths) {
|
package/dist/output/format.js
CHANGED
|
@@ -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.
|
|
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
|
-
|
|
185
|
+
공개 설치: npm install -g jutell
|
|
186
|
+
업데이트: npm update -g jutell`);
|
|
187
187
|
}
|
package/dist/process/mcpProbe.js
CHANGED
|
@@ -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.
|
|
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: {} });
|