jutell 1.0.1 → 2.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.
@@ -1,48 +1,48 @@
1
- # Request Builder — 요청 만들기 템플릿
2
-
3
- ## 이 폴더는 무엇인가요?
4
-
5
- 막연한 요구를 AI Agent가 이해할 수 있는 요청서로 바꾸는 Markdown 템플릿 모음입니다.
6
-
7
- JuTell은 Agent를 대신 실행하거나 Prompt를 자동으로 보내지 않습니다. 완성된 요청문을 복사해서 사용 중인 AI Agent(Codex, Claude Code, OpenCode 등)에 직접 붙여넣으면 됩니다.
8
-
9
- ## 사용 방법 (3단계)
10
-
11
- 1. 하고 싶은 일과 가장 가까운 템플릿을 고릅니다.
12
- 2. `[ ]` 선택지에 체크하고, 빈칸에는 쉬운 말로 적습니다. 모르는 항목은 **비워 둡니다.**
13
- 3. 맨 아래 **최종 요청문**을 복사해 AI Agent에 붙여넣습니다.
14
-
15
- ## 템플릿 목록
16
-
17
- | 템플릿 | 언제 사용하나요? |
18
- |---|---|
19
- | [DESIGN_REQUEST.md](DESIGN_REQUEST.md) | 화면 모양이나 분위기를 바꾸고 싶을 때 |
20
- | [FEATURE_REQUEST.md](FEATURE_REQUEST.md) | 새로운 기능이나 화면을 만들고 싶을 때 |
21
- | [BUG_REPORT_REQUEST.md](BUG_REPORT_REQUEST.md) | 무언가 고장 났거나 이상하게 동작할 때 |
22
- | [PROJECT_PLANNING_REQUEST.md](PROJECT_PLANNING_REQUEST.md) | 프로젝트 전체를 처음 계획할 때 |
23
- | [CODE_REVIEW_REQUEST.md](CODE_REVIEW_REQUEST.md) | 만든 작업을 검토받고 싶을 때 |
24
- | [NEXT_AGENT_HANDOFF.md](NEXT_AGENT_HANDOFF.md) | 다른 AI Agent에게 작업을 넘길 때 |
25
- | [MANUAL_EDIT_GUIDE.md](MANUAL_EDIT_GUIDE.md) | 직접 파일을 수정해야 할 때 |
26
-
27
- ## 공통 선택지 의미
28
-
29
- - **잘 모르겠음** — 해당 항목을 모르며 AI의 도움을 원합니다.
30
- - **정확히 무엇이 문제인지 모르겠음** — 문제는 느끼지만 원인을 특정하지 못합니다.
31
- - **현재 상태 유지** — 이 부분은 지금 그대로 두길 원합니다.
32
- - **요청하지 않음** — 이 항목은 다루지 않길 원합니다.
33
- - **AI가 먼저 확인 후 제안** — 답을 추측하지 말고 먼저 확인한 뒤 선택지를 제안해달라는 뜻입니다.
34
-
35
- ## 작성 원칙
36
-
37
- - 전문 용어, 색상 코드(Hex), CSS, Framework 이름을 몰라도 됩니다. 그런 용어가 필요한 항목은 선택지로 고르거나 `잘 모르겠음`으로 두면 됩니다.
38
- - **모르는 항목은 추측해서 채우지 않습니다.** 빈칸은 AI가 요청서를 읽고 질문해야 할 항목으로 남습니다.
39
- - **사용자가 직접 쓴 사실과 AI의 제안은 구분해서 표시합니다.**
40
-
41
- ## 프로젝트 경로 안내 주의
42
-
43
- 템플릿 안에 "파일은 `src/css/`에 있음", "`npm run dev`로 실행함" 같은 내용을 임의로 채우면 안 됩니다. 실제 프로젝트 구조를 확인하기 전에는 특정 경로나 실행 명령을 확정해 적지 않고, 모르면 `확인 필요`로 남깁니다. AI가 먼저 프로젝트를 확인한 뒤 안내하도록 유도하는 것이 이 템플릿의 목적입니다.
44
-
45
- ## 요청하기 전에
46
-
47
- - 비밀번호, API 키, 토큰, 실제 개인정보는 요청서에 적지 않습니다.
48
- - 기밀 프로젝트 이름이나 저장소 주소가 필요하다면 `내용 요약`만 적습니다.
1
+ # Request Builder — 요청 만들기 템플릿
2
+
3
+ ## 이 폴더는 무엇인가요?
4
+
5
+ 막연한 요구를 AI Agent가 이해할 수 있는 요청서로 바꾸는 Markdown 템플릿 모음입니다.
6
+
7
+ JuTell은 Agent를 대신 실행하거나 Prompt를 자동으로 보내지 않습니다. 완성된 요청문을 복사해서 사용 중인 AI Agent(Codex, Claude Code, OpenCode 등)에 직접 붙여넣으면 됩니다.
8
+
9
+ ## 사용 방법 (3단계)
10
+
11
+ 1. 하고 싶은 일과 가장 가까운 템플릿을 고릅니다.
12
+ 2. `[ ]` 선택지에 체크하고, 빈칸에는 쉬운 말로 적습니다. 모르는 항목은 **비워 둡니다.**
13
+ 3. 맨 아래 **최종 요청문**을 복사해 AI Agent에 붙여넣습니다.
14
+
15
+ ## 템플릿 목록
16
+
17
+ | 템플릿 | 언제 사용하나요? |
18
+ |---|---|
19
+ | [DESIGN_REQUEST.md](DESIGN_REQUEST.md) | 화면 모양이나 분위기를 바꾸고 싶을 때 |
20
+ | [FEATURE_REQUEST.md](FEATURE_REQUEST.md) | 새로운 기능이나 화면을 만들고 싶을 때 |
21
+ | [BUG_REPORT_REQUEST.md](BUG_REPORT_REQUEST.md) | 무언가 고장 났거나 이상하게 동작할 때 |
22
+ | [PROJECT_PLANNING_REQUEST.md](PROJECT_PLANNING_REQUEST.md) | 프로젝트 전체를 처음 계획할 때 |
23
+ | [CODE_REVIEW_REQUEST.md](CODE_REVIEW_REQUEST.md) | 만든 작업을 검토받고 싶을 때 |
24
+ | [NEXT_AGENT_HANDOFF.md](NEXT_AGENT_HANDOFF.md) | 다른 AI Agent에게 작업을 넘길 때 |
25
+ | [MANUAL_EDIT_GUIDE.md](MANUAL_EDIT_GUIDE.md) | 직접 파일을 수정해야 할 때 |
26
+
27
+ ## 공통 선택지 의미
28
+
29
+ - **잘 모르겠음** — 해당 항목을 모르며 AI의 도움을 원합니다.
30
+ - **정확히 무엇이 문제인지 모르겠음** — 문제는 느끼지만 원인을 특정하지 못합니다.
31
+ - **현재 상태 유지** — 이 부분은 지금 그대로 두길 원합니다.
32
+ - **요청하지 않음** — 이 항목은 다루지 않길 원합니다.
33
+ - **AI가 먼저 확인 후 제안** — 답을 추측하지 말고 먼저 확인한 뒤 선택지를 제안해달라는 뜻입니다.
34
+
35
+ ## 작성 원칙
36
+
37
+ - 전문 용어, 색상 코드(Hex), CSS, Framework 이름을 몰라도 됩니다. 그런 용어가 필요한 항목은 선택지로 고르거나 `잘 모르겠음`으로 두면 됩니다.
38
+ - **모르는 항목은 추측해서 채우지 않습니다.** 빈칸은 AI가 요청서를 읽고 질문해야 할 항목으로 남습니다.
39
+ - **사용자가 직접 쓴 사실과 AI의 제안은 구분해서 표시합니다.**
40
+
41
+ ## 프로젝트 경로 안내 주의
42
+
43
+ 템플릿 안에 "파일은 `src/css/`에 있음", "`npm run dev`로 실행함" 같은 내용을 임의로 채우면 안 됩니다. 실제 프로젝트 구조를 확인하기 전에는 특정 경로나 실행 명령을 확정해 적지 않고, 모르면 `확인 필요`로 남깁니다. AI가 먼저 프로젝트를 확인한 뒤 안내하도록 유도하는 것이 이 템플릿의 목적입니다.
44
+
45
+ ## 요청하기 전에
46
+
47
+ - 비밀번호, API 키, 토큰, 실제 개인정보는 요청서에 적지 않습니다.
48
+ - 기밀 프로젝트 이름이나 저장소 주소가 필요하다면 `내용 요약`만 적습니다.
@@ -1,5 +1,5 @@
1
1
  {
2
- "cli": "1.0.1",
2
+ "cli": "2.0.0",
3
3
  "skill": "확인 필요",
4
4
  "mcp": "0.1.0",
5
5
  "admin": "0.1.0"
@@ -1,6 +1,6 @@
1
1
  import { stdin } from 'node:process';
2
2
  import { dashboardCommand } from './dashboard.js';
3
- import { enableCommand, setupCommand } from './lifecycle.js';
3
+ import { enableCommand } from './lifecycle.js';
4
4
  import { getStatus } from './status.js';
5
5
  import { useCommand } from './use.js';
6
6
  import { AGENT_PROVIDERS } from '../installer/providers.js';
@@ -10,85 +10,144 @@ const profileLabels = {
10
10
  learning: '학습 보고',
11
11
  detailed: '상세 보고',
12
12
  };
13
- const profileChoices = [
14
- { value: 'balanced', label: '균형 보고', note: '처음 사용에 적당한 기본값 (권장)' },
15
- { value: 'minimal', label: '최소 보고', note: '핵심만 짧게' },
16
- { value: 'learning', label: '학습 보고', note: '개발 용어를 조금 더 설명' },
17
- { value: 'detailed', label: '상세 보고', note: '복잡한 작업을 자세히' },
18
- ];
19
13
  function needsRepair(status) {
20
14
  return !status.configValid || !status.skillInstalled || !status.agentsManaged || status.codexPreparation !== 'enabled';
21
15
  }
22
16
  function readyMessage(status) {
23
17
  const ready = status.configValid && status.skillInstalled && status.agentsManaged && status.codexPreparation === 'enabled';
24
18
  if (!ready) {
25
- return `JuTell 연결이 일부 준비되지 않았습니다.
26
-
27
- 설정과 Skill을 확인했지만 AI Agent Provider 연결 준비가 완료되지 않았습니다.
19
+ return `JuTell 연결이 일부 준비되지 않았습니다.
20
+
21
+ 설정과 Skill을 확인했지만 AI Agent Provider 연결 준비가 완료되지 않았습니다.
28
22
  관리자 화면에서 현재 상태를 확인할 수 있습니다.`;
29
23
  }
30
- return `JuTell 준비 완료
31
-
32
- ✓ 설정 연결됨
33
- ✓ Skill 연결됨
34
- ✓ AI Agent 연결 준비 완료
35
- ✓ 안전 보고 규칙 적용됨
36
-
37
- 현재 보고 방식: ${profileLabels[status.profile]}
38
- 활성 기능: ${status.activeFeatureCount}개
39
-
40
- 새 AI Agent 세션부터 사용할 수 있습니다.
24
+ return `JuTell 준비 완료
25
+
26
+ ✓ 설정 연결됨
27
+ ✓ Skill 연결됨
28
+ ✓ AI Agent 연결 준비 완료
29
+ ✓ 안전 보고 규칙 적용됨
30
+
31
+ 현재 보고 방식: ${profileLabels[status.profile]}
32
+ 활성 기능: ${status.activeFeatureCount}개
33
+
34
+ 새 AI Agent 세션부터 사용할 수 있습니다.
41
35
  실제 도구 호출 여부는 해당 Provider에서 확인할 수 있습니다.`;
42
36
  }
43
- const firstRunMessage = `이 프로젝트에는 아직 JuTell이 연결되지 않았습니다.
44
-
45
- JuTell을 연결하면:
46
- - AI 작업을 쉬운 말로 보고받을 수 있습니다.
47
- - 현재 설정에 맞춰 보고 길이와 설명 방식을 조절할 수 있습니다.
48
- - 연결된 AI Agent에서 JuTell MCP를 사용할 수 있습니다.`;
49
- async function firstRunWizard(io) {
50
- io.write(`Welcome to JuTell! 🎉
51
-
52
- AI가 한 일을 쉽게 이해하고, 검증하고, 다음 작업으로 이어가도록 돕습니다.
53
- 몇 가지만 고르면 이 프로젝트에 연결을 준비합니다.
54
- `);
55
- const agentChoices = AGENT_PROVIDERS.map((provider) => ({
56
- value: provider.id,
57
- label: provider.label,
58
- note: provider.status === 'planned' ? '준비 중' : provider.description,
59
- }));
60
- let agent = 'codex';
61
- while (true) {
62
- const picked = await io.choose('① 사용 중인 AI Agent를 선택하세요.', agentChoices, 'codex');
63
- const provider = AGENT_PROVIDERS.find((item) => item.id === picked);
64
- if (provider && provider.status !== 'planned') {
65
- agent = provider.id;
66
- break;
37
+ export function providerRuntimeStatuses(status) {
38
+ const byId = (id) => AGENT_PROVIDERS.find((provider) => provider.id === id);
39
+ const rows = [
40
+ ['codex', status.codexDetected, status.codexPreparation === 'enabled', status.codexPreparation === 'error'],
41
+ ['opencode', status.opencodeDetected, status.opencodePreparation === 'enabled', status.opencode.conflict],
42
+ ['claude-code', status.claudeDetected, status.claudePreparation === 'enabled', status.claude.conflict],
43
+ ];
44
+ return rows
45
+ .map(([id, detected, connected, conflict]) => {
46
+ const provider = byId(id);
47
+ return provider ? { provider, detected, connected, conflict } : undefined;
48
+ })
49
+ .filter((row) => row !== undefined);
50
+ }
51
+ function padLabel(label, width) {
52
+ return label + ' '.repeat(Math.max(1, width - label.length + 2));
53
+ }
54
+ const noProvidersMessage = `JuTell이 이 컴퓨터에서 지원하는 Coding Agent(Codex, OpenCode, Claude Code)를 찾지 못했습니다.
55
+
56
+ Codex, OpenCode, Claude Code 중 하나를 설치한 뒤 'jutell'을 다시 실행해 주세요.
57
+ 이미 설치했다면 PATH에서 실행 파일을 찾을 수 있는지 'jutell doctor'로 확인해 주세요.`;
58
+ function alreadyConfiguredMessage(statuses) {
59
+ const width = Math.max(...statuses.map((row) => row.provider.label.length));
60
+ const lines = statuses.map((row) => `${padLabel(row.provider.label, width)}${row.detected ? '연결됨' : '미감지'}`);
61
+ return `JuTell
62
+
63
+ ${lines.join('\n')}
64
+
65
+ JuTell 준비 완료.
66
+
67
+ 명령 목록: jutell --help`;
68
+ }
69
+ // Printed once, after offerAutoConnect() finishes successfully, instead of auto-launching
70
+ // the dashboard (see JUTELL-V1.X-AUTO-SETUP-FOUNDATION-01B - the user asked to connect,
71
+ // not to open a browser/local-server session; `jutell dashboard` remains the explicit,
72
+ // opt-in way to do that). Lists every currently-connected provider, not just the ones
73
+ // connected in this run, so a partial-catch-up run still shows the full accurate picture.
74
+ function connectedSummaryMessage(statuses) {
75
+ const connectedLines = statuses.filter((row) => row.connected).map((row) => `✓ ${row.provider.label} 연결됨`);
76
+ return `JuTell 준비 완료.
77
+
78
+ ${connectedLines.join('\n')}
79
+
80
+ 새 Coding Agent 세션을 열고 평소처럼 사용하세요.
81
+
82
+ 'jutell' — 상태/설정
83
+ 'jutell --help' — 명령어 보기`;
84
+ }
85
+ /**
86
+ * Multi-provider auto-detect-and-connect for bare `jutell`. Reuses the exact
87
+ * same per-provider detection (via getStatus, which already wraps
88
+ * codexDetected/opencodeDetected/claudeDetected) and the exact same atomic,
89
+ * idempotent, rollback-safe registration path `jutell use <provider>` already
90
+ * uses (useCommand) - this intentionally does not implement a second
91
+ * detection or registration system.
92
+ */
93
+ export async function offerAutoConnect(paths, options, io, statuses) {
94
+ const detected = statuses.filter((row) => row.detected);
95
+ const toConnect = detected.filter((row) => !row.connected);
96
+ const offerable = toConnect.filter((row) => !row.conflict);
97
+ const conflicted = toConnect.filter((row) => row.conflict);
98
+ const width = Math.max(...statuses.map((row) => row.provider.label.length));
99
+ const foundList = statuses
100
+ .map((row) => `${padLabel(row.provider.label, width)}${row.detected ? (row.connected ? '찾음 (이미 연결됨)' : '찾음') : '미감지'}`)
101
+ .join('\n');
102
+ io.write(`JuTell\n\n찾은 Coding Agent:\n\n${foundList}`);
103
+ if (offerable.length === 0) {
104
+ if (conflicted.length) {
105
+ io.write(`\n주의: ${conflicted.map((row) => row.provider.label).join(', ')}에 관리되지 않는 기존 MCP 설정이 있어 자동으로 연결하지 않았습니다. 'jutell use <agent>'로 직접 확인해 주세요.`);
67
106
  }
68
- io.write(`${provider?.label ?? '선택한 Agent'}는 아직 준비 중입니다. 지금 사용할 수 있는 Agent를 선택하세요.`);
107
+ return { cancelled: true };
69
108
  }
70
- const profile = (await io.choose('② 보고 방식을 선택하세요. 나중에 언제든 바꿀 수 있습니다.', profileChoices, 'balanced'));
71
- const agentLabel = AGENT_PROVIDERS.find((item) => item.id === agent)?.label ?? agent;
72
- io.write(`\n③ 선택 완료: ${agentLabel} · ${profileLabels[profile]}\n연결을 준비합니다.\n`);
73
- return { agent, profile };
74
- }
75
- export async function defaultCommand(paths, options, io) {
76
- let status = await getStatus(paths);
77
- if (!status.configExists) {
78
- if (options.yes || stdin.isTTY !== true) {
79
- io.write(firstRunMessage);
80
- if (!options.yes && !(await io.ask('이 프로젝트에 연결할까요?', true))) {
81
- io.write('JuTell을 연결하지 않았습니다.');
82
- return { cancelled: true };
83
- }
84
- await setupCommand(paths, { ...options, yes: true, oneCommand: true, activateMcp: true }, io);
109
+ io.write('\nJuTell을 이 Agent들에 연결할 수 있습니다.');
110
+ if (!options.yes) {
111
+ if (stdin.isTTY !== true) {
112
+ io.write("\n대화형 터미널이 아니어서 확인 없이 연결하지 않았습니다.\n자동으로 연결하려면 'jutell --yes'를 실행하거나, 대화형 터미널에서 'jutell'을 다시 실행해 주세요.");
113
+ return { cancelled: true };
85
114
  }
86
- else {
87
- const picked = await firstRunWizard(io);
88
- await useCommand(paths, { ...options, yes: true, profile: picked.profile }, io, ['use', picked.agent]);
115
+ const confirmed = await io.ask('JuTell을 지금 연결할까요?', true);
116
+ if (!confirmed) {
117
+ io.write("\nJuTell을 연결하지 않았습니다.\n나중에 다시 'jutell'을 실행하면 같은 안내를 볼 수 있습니다.");
118
+ return { cancelled: true };
89
119
  }
90
120
  }
91
- else if (needsRepair(status)) {
121
+ io.write('\nJuTell을 연결하는 중...\n');
122
+ for (const row of offerable) {
123
+ await useCommand(paths, { ...options, yes: true }, io, ['use', row.provider.id]);
124
+ }
125
+ if (conflicted.length) {
126
+ io.write(`주의: ${conflicted.map((row) => row.provider.label).join(', ')}에 관리되지 않는 기존 MCP 설정이 있어 자동으로 연결하지 않았습니다. 'jutell use <agent>'로 직접 확인해 주세요.`);
127
+ }
128
+ return { cancelled: false };
129
+ }
130
+ export async function defaultCommand(paths, options, io) {
131
+ const status = await getStatus(paths);
132
+ const statuses = providerRuntimeStatuses(status);
133
+ const detected = statuses.filter((row) => row.detected);
134
+ const toConnect = detected.filter((row) => !row.connected);
135
+ if (detected.length === 0) {
136
+ io.write(noProvidersMessage);
137
+ return { cancelled: true };
138
+ }
139
+ if (toConnect.length > 0) {
140
+ const result = await offerAutoConnect(paths, options, io, statuses);
141
+ if (result.cancelled)
142
+ return result;
143
+ // Connected - print a concise summary and return to the shell. Does NOT
144
+ // auto-launch the dashboard (JUTELL-V1.X-AUTO-SETUP-FOUNDATION-01B): the
145
+ // user asked to connect, not to open a local browser/server session.
146
+ // `jutell dashboard` remains the explicit, unchanged, opt-in way to do that.
147
+ io.write(connectedSummaryMessage(providerRuntimeStatuses(await getStatus(paths))));
148
+ return { cancelled: false };
149
+ }
150
+ if (needsRepair(status)) {
92
151
  io.write('JuTell 연결이 일부 준비되지 않았습니다.\nSkill, AGENTS.md, AI Agent Provider 연결 준비를 안전하게 확인할 수 있습니다.');
93
152
  if (!options.yes && !(await io.ask('다시 켜고 관리자 화면을 열까요?', true))) {
94
153
  io.write('현재 설정을 그대로 두고 관리자 화면을 엽니다.');
@@ -96,8 +155,12 @@ export async function defaultCommand(paths, options, io) {
96
155
  else {
97
156
  await enableCommand(paths, { ...options, yes: true, oneCommand: true }, io);
98
157
  }
158
+ io.write(readyMessage(await getStatus(paths)));
159
+ return dashboardCommand(paths, options, io);
99
160
  }
100
- status = await getStatus(paths);
101
- io.write(readyMessage(status));
102
- return dashboardCommand(paths, options, io);
161
+ // Every detected provider is already connected and nothing needs repair -
162
+ // this is a repeat run, not onboarding. Keep it minimal: no wizard, no
163
+ // dashboard auto-launch, just a status line and a pointer to --help.
164
+ io.write(alreadyConfiguredMessage(statuses));
165
+ return { cancelled: false };
103
166
  }
@@ -58,24 +58,24 @@ async function statusCommand(paths, io) {
58
58
  const codex = await readCodexRegistration(codexScopedPaths(paths), packageRoot(), false);
59
59
  const opencode = await readOpenCodeRegistration(paths, packageRoot(), false);
60
60
  const claude = await readClaudeRegistration(paths, packageRoot(), false);
61
- io.write(`JuTell AI Agent Provider 상태
62
-
63
- ${providerLabel('codex')}
64
- 감지: ${codexDetected() ? '예' : '직접 확인 필요'}
65
- 설정 파일: 사용자 전역 Codex 설정 (Codex는 프로젝트 설정을 읽지 않음)
66
- MCP 등록: ${codex.registered ? '등록됨' : codex.conflict ? '충돌 확인 필요' : '등록되지 않음'}
67
- 활성화: ${codex.registered ? (codex.enabled ? '예' : '아니오') : '등록되지 않음'}
68
-
69
- ${providerLabel('opencode')}
70
- 감지: ${opencodeDetected() ? '예' : '직접 확인 필요'}
71
- 설정 파일: ${openCodeScopeLabel(paths)}
72
- MCP 등록: ${opencode.registered ? '등록됨' : opencode.conflict ? '충돌 확인 필요' : '등록되지 않음'}
73
- 활성화: ${opencode.registered ? (opencode.enabled ? '예' : '아니오') : '등록되지 않음'}
74
-
75
- ${providerLabel('claude-code')}
76
- 감지: ${claudeDetected() ? '예' : '직접 확인 필요'}
77
- 설정 파일: ${claudeScopeLabel(paths)}
78
- MCP 등록: ${claude.registered ? '등록됨' : '등록되지 않음'}
61
+ io.write(`JuTell AI Agent Provider 상태
62
+
63
+ ${providerLabel('codex')}
64
+ 감지: ${codexDetected() ? '예' : '직접 확인 필요'}
65
+ 설정 파일: 사용자 전역 Codex 설정 (Codex는 프로젝트 설정을 읽지 않음)
66
+ MCP 등록: ${codex.registered ? '등록됨' : codex.conflict ? '충돌 확인 필요' : '등록되지 않음'}
67
+ 활성화: ${codex.registered ? (codex.enabled ? '예' : '아니오') : '등록되지 않음'}
68
+
69
+ ${providerLabel('opencode')}
70
+ 감지: ${opencodeDetected() ? '예' : '직접 확인 필요'}
71
+ 설정 파일: ${openCodeScopeLabel(paths)}
72
+ MCP 등록: ${opencode.registered ? '등록됨' : opencode.conflict ? '충돌 확인 필요' : '등록되지 않음'}
73
+ 활성화: ${opencode.registered ? (opencode.enabled ? '예' : '아니오') : '등록되지 않음'}
74
+
75
+ ${providerLabel('claude-code')}
76
+ 감지: ${claudeDetected() ? '예' : '직접 확인 필요'}
77
+ 설정 파일: ${claudeScopeLabel(paths)}
78
+ MCP 등록: ${claude.registered ? '등록됨' : '등록되지 않음'}
79
79
  활성화: ${claude.registered ? '예' : '등록되지 않음'}`);
80
80
  }
81
81
  function requireTarget(args) {
@@ -35,22 +35,22 @@ export async function sessionStatusCommand(paths, io) {
35
35
  return 0;
36
36
  }
37
37
  export async function sessionHelpCommand(io) {
38
- io.write(`Session은 하루 작업 기록을 담는 폴더입니다.
39
- Page는 Agent·역할별 파일, Work는 Page 안의 작업입니다.
40
-
41
- 하루 기록 명령
42
-
43
- jutell session 오늘 기록 상태를 봅니다.
44
- jutell session new 오늘 Session 폴더를 만듭니다.
45
- jutell session page Agent·역할별 Page 파일을 만듭니다.
46
- jutell session work 현재 Page에 다음 번호 작업을 추가합니다.
47
- jutell session move 작업할 Page를 이동합니다.
48
- jutell session finish SESSION_SUMMARY.md로 하루를 마감합니다.
49
-
50
- 운영자용 (보통 사용할 필요가 없습니다)
51
-
52
- jutell session storage Session 저장 위치 상태를 확인합니다.
53
- jutell session storage set <절대 경로> Session 저장 위치를 운영자 지정으로 바꿉니다.
38
+ io.write(`Session은 하루 작업 기록을 담는 폴더입니다.
39
+ Page는 Agent·역할별 파일, Work는 Page 안의 작업입니다.
40
+
41
+ 하루 기록 명령
42
+
43
+ jutell session 오늘 기록 상태를 봅니다.
44
+ jutell session new 오늘 Session 폴더를 만듭니다.
45
+ jutell session page Agent·역할별 Page 파일을 만듭니다.
46
+ jutell session work 현재 Page에 다음 번호 작업을 추가합니다.
47
+ jutell session move 작업할 Page를 이동합니다.
48
+ jutell session finish SESSION_SUMMARY.md로 하루를 마감합니다.
49
+
50
+ 운영자용 (보통 사용할 필요가 없습니다)
51
+
52
+ jutell session storage Session 저장 위치 상태를 확인합니다.
53
+ jutell session storage set <절대 경로> Session 저장 위치를 운영자 지정으로 바꿉니다.
54
54
  jutell session storage reset 운영자 지정을 제거하고 기본 위치로 돌아갑니다.`);
55
55
  return 0;
56
56
  }
@@ -2,14 +2,16 @@ import { readText, writeTextSafely } from '../config/managed.js';
2
2
  import path from 'node:path';
3
3
  export const JUTELL_AGENTS_BEGIN = '<!-- BEGIN JUTELL MANAGED BLOCK -->';
4
4
  export const JUTELL_AGENTS_END = '<!-- END JUTELL MANAGED BLOCK -->';
5
- const managedBlock = `${JUTELL_AGENTS_BEGIN}
6
- ## JuTell
7
-
8
- - 코드 또는 문서 변경 후 \`.agents/skills/beginner-bridge/SKILL.md\`와 \`.jutell.json\`을 먼저 읽습니다. 소유자 대상 구현/보고 전에는 JuTell 보고 규칙을 먼저 적용해 최종 답변을 작성합니다.
9
- - JuTell MCP가 보이면 canonical \`jutell\` 서버를 사용합니다. \`jutell\`과 legacy \`beginner_bridge\`가 모두 보이면 \`jutell\`을 우선하고 \`beginner_bridge\`는 호환용으로만 사용합니다.
10
- - 확인하지 않은 결과를 사실처럼 표현하지 않습니다.
11
- - 비밀정보를 명령 출력이나 보고서에 포함하지 않습니다.
12
- - 외부 전송 없이 현재 프로젝트의 로컬 설정을 기준으로 작업합니다.
5
+ const managedBlock = `${JUTELL_AGENTS_BEGIN}
6
+ ## JuTell
7
+
8
+ - 코드 또는 문서 변경 후 \`.agents/skills/beginner-bridge/SKILL.md\`와 \`.jutell.json\`을 먼저 읽습니다. 소유자 대상 구현/보고 전에는 JuTell 보고 규칙을 먼저 적용해 최종 답변을 작성합니다.
9
+ - 실제로 코드나 문서를 바꾸기 전에는, 요청에 결과를 크게 바꿀 수 있는 불확실함이 있는지 SKILL.md의 Intent Bridge 규칙으로 판단합니다. 불확실함이 없거나 \`requestClarificationGuide\`가 꺼져 있으면 평소처럼 바로 진행합니다.
10
+ - JuTell MCP가 보이면 canonical \`jutell\` 서버를 사용합니다. \`jutell\`과 legacy \`beginner_bridge\`가 모두 보이면 \`jutell\`을 우선하고 \`beginner_bridge\`는 호환용으로만 사용합니다.
11
+ - JuTell MCP를 사용할 수 있고 이미 확보한 근거로 보고·검증·핸드오프의 모호함을 줄여줄 때는 그 도구를 우선합니다. 사용할 수 없거나 Provider 정책으로 막혀 있으면 작업을 멈추지 않고 JuTell Skill로 계속합니다. 실제로 호출해 응답을 받은 경우에만 JuTell MCP를 사용했다고 말합니다.
12
+ - 확인하지 않은 결과를 사실처럼 표현하지 않습니다.
13
+ - 비밀정보를 명령 출력이나 보고서에 포함하지 않습니다.
14
+ - 외부 전송 없이 현재 프로젝트의 로컬 설정을 기준으로 작업합니다.
13
15
  ${JUTELL_AGENTS_END}`;
14
16
  function markerPattern() {
15
17
  return new RegExp(`${JUTELL_AGENTS_BEGIN}[\\s\\S]*?${JUTELL_AGENTS_END}`, 'm');
@@ -140,48 +140,46 @@ 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 1.0.1
144
-
145
- 시작할 때는 jutell만 입력하면 됩니다.
146
- 처음 연결하면 안내에 따라 AI Agent와 보고 방식을 고르고
147
- 연결을 준비한 뒤 관리자 화면을 엽니다.
148
-
149
- 자주 쓰는 명령
150
-
151
- jutell 처음 시작: 설치·연결·관리자 화면을 준비합니다.
152
- jutell use codex Codex에 연결합니다 (권장).
153
- jutell use opencode OpenCode에 연결합니다.
154
- jutell use claude Claude Code에 연결합니다 (베타).
155
- jutell status 현재 연결 상태를 확인합니다.
156
- jutell doctor 문제가 있는지 점검합니다.
157
- jutell on 연결을 켭니다.
158
- jutell off 연결을 끕니다.
159
-
160
- 연결 후 새 대화를 열면 JuTell이 자동으로 적용됩니다.
161
- JuTell은 AI Agent를 대신 실행하지 않고 연결과 보고만 도와줍니다.
162
-
163
- 고급 명령 (보통 사용할 필요가 없습니다)
164
-
165
- jutell dashboard 관리자 화면만 엽니다.
166
- jutell setup 설치를 다시 진행합니다.
167
- jutell enable 연결을 켭니다 (on과 같음).
168
- jutell disable 연결을 끕니다 (off와 같음).
169
- jutell provider Agent 연결 상태를 자세히 봅니다.
170
- jutell connect 연결만 추가합니다.
171
- jutell disconnect 해당 연결만 끕니다.
172
- jutell switch 기본 Agent를 전환합니다.
173
- jutell uninstall 설치를 제거합니다.
174
- jutell upgrade 설치된 Skill/설정/MCP를 최신으로 새로고침합니다.
175
- jutell migrate 레거시 beginner_bridge → jutell 로 안전하게 옮깁니다.
176
- jutell migrate --clean 레거시 정리를 수행합니다 (canonical 확인 후).
177
-
178
- 하루 작업 기록
179
-
180
- jutell session 오늘 기록 상태를 봅니다.
181
- jutell session help 하루 기록의 하위 명령을 보여줍니다.
182
-
183
- 이전 별칭: beginner-bridge
184
-
185
- 공개 설치: npm install -g jutell
143
+ io.write(`JuTell CLI 2.0.0
144
+
145
+ 시작할 때는 jutell만 입력하면 됩니다.
146
+ 설치된 Coding Agent(Codex, OpenCode, Claude Code)를 찾아 연결하고,
147
+ 곧바로 평소 쓰던 화면으로 돌아갑니다. 관리자 화면은 열지 않습니다.
148
+
149
+ 자주 쓰는 명령
150
+
151
+ jutell 처음 시작: 설치된 Agent를 찾아 연결합니다.
152
+ jutell use codex Codex에 연결합니다 (권장).
153
+ jutell use opencode OpenCode에 연결합니다 (베타).
154
+ jutell use claude Claude Code에 연결합니다 (베타).
155
+ jutell status 현재 연결 상태를 확인합니다.
156
+ jutell doctor 문제가 있는지 점검합니다.
157
+ jutell on 연결을 켭니다.
158
+ jutell off 연결을 끕니다.
159
+
160
+ 연결 후 새 대화를 열면 JuTell이 자동으로 적용됩니다.
161
+ JuTell은 AI Agent를 대신 실행하지 않고 연결과 보고만 도와줍니다.
162
+
163
+ 고급 명령 (보통 사용할 필요가 없습니다)
164
+
165
+ jutell dashboard 관리자 화면을 엽니다 (필요할 때만 직접 실행).
166
+ jutell setup 설치를 다시 진행합니다.
167
+ jutell enable 연결을 켭니다 (on과 같음).
168
+ jutell disable 연결을 끕니다 (off와 같음).
169
+ jutell provider Agent 연결 상태를 자세히 봅니다.
170
+ jutell connect 연결만 추가합니다.
171
+ jutell disconnect 해당 연결만 끕니다.
172
+ jutell switch 기본 Agent를 전환합니다.
173
+ jutell uninstall 설치를 제거합니다.
174
+ jutell upgrade 설치된 Skill/설정/MCP를 최신으로 새로고침합니다.
175
+ jutell migrate 레거시 beginner_bridge → jutell 로 안전하게 옮깁니다. (이전 명령 이름: beginner-bridge)
176
+ jutell migrate --clean 레거시 정리를 수행합니다 (canonical 확인 후).
177
+
178
+ 하루 작업 기록
179
+
180
+ jutell session 오늘 기록 상태를 봅니다.
181
+ jutell session help 하루 기록의 하위 명령을 보여줍니다.
182
+
183
+ 공개 설치: npm install -g jutell
186
184
  업데이트: npm update -g jutell`);
187
185
  }
@@ -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: '1.0.1' } },
9
+ params: { protocolVersion: PROTOCOL_VERSION, capabilities: {}, clientInfo: { name: 'jutell-doctor', version: '2.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: {} });