triflux 10.3.4 → 10.7.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/LICENSE +21 -21
- package/bin/tfx-doctor-tui.mjs +1 -1
- package/bin/tfx-doctor.mjs +6 -1
- package/bin/tfx-profile.mjs +1 -1
- package/bin/tfx-setup-tui.mjs +1 -1
- package/bin/tfx-setup.mjs +6 -1
- package/bin/triflux.mjs +2396 -1140
- package/hooks/agent-route-guard.mjs +12 -8
- package/hooks/cross-review-tracker.mjs +21 -8
- package/hooks/error-context.mjs +19 -7
- package/hooks/hook-adaptive-collector.mjs +18 -16
- package/hooks/hook-manager.mjs +93 -32
- package/hooks/hook-orchestrator.mjs +108 -24
- package/hooks/hook-registry.json +11 -0
- package/hooks/keyword-rules.json +6 -10
- package/hooks/lib/resolve-root.mjs +1 -1
- package/hooks/mcp-config-watcher.mjs +6 -2
- package/hooks/pipeline-stop.mjs +3 -6
- package/hooks/safety-guard.mjs +99 -28
- package/hooks/session-start-fast.mjs +143 -0
- package/hooks/subagent-verifier.mjs +5 -4
- package/hub/account-broker.mjs +256 -60
- package/hub/adaptive-diagnostic.mjs +75 -48
- package/hub/adaptive-inject.mjs +95 -57
- package/hub/adaptive-memory.mjs +156 -42
- package/hub/adaptive.mjs +60 -31
- package/hub/assign-callbacks.mjs +67 -30
- package/hub/bridge.mjs +0 -1
- package/hub/cli-adapter-base.mjs +200 -48
- package/hub/codex-adapter.mjs +76 -96
- package/hub/codex-compat.mjs +3 -3
- package/hub/codex-preflight.mjs +63 -37
- package/hub/delegator/contracts.mjs +19 -23
- package/hub/delegator/index.mjs +3 -3
- package/hub/delegator/service.mjs +88 -64
- package/hub/delegator/tool-definitions.mjs +5 -5
- package/hub/fullcycle.mjs +33 -17
- package/hub/gemini-adapter.mjs +69 -94
- package/hub/hitl.mjs +89 -30
- package/hub/intent.mjs +161 -38
- package/hub/lib/cache-guard.mjs +43 -17
- package/hub/lib/mcp-response-cache.mjs +66 -32
- package/hub/lib/memory-store.mjs +285 -111
- package/hub/lib/path-utils.mjs +35 -37
- package/hub/lib/process-utils.mjs +106 -37
- package/hub/lib/spawn-trace.mjs +527 -0
- package/hub/lib/ssh-command.mjs +34 -4
- package/hub/lib/ssh-retry.mjs +5 -1
- package/hub/lib/uuidv7.mjs +4 -3
- package/hub/memory-doctor.mjs +266 -106
- package/hub/middleware/request-logger.mjs +61 -34
- package/hub/paths.mjs +9 -9
- package/hub/pipeline/gates/confidence.mjs +34 -15
- package/hub/pipeline/gates/consensus.mjs +27 -15
- package/hub/pipeline/gates/index.mjs +7 -3
- package/hub/pipeline/gates/selfcheck.mjs +57 -19
- package/hub/pipeline/index.mjs +77 -42
- package/hub/pipeline/state.mjs +10 -10
- package/hub/pipeline/transitions.mjs +40 -23
- package/hub/platform.mjs +57 -48
- package/hub/promote-penalties.mjs +25 -7
- package/hub/quality/deslop.mjs +70 -49
- package/hub/research.mjs +32 -25
- package/hub/router.mjs +240 -107
- package/hub/routing/complexity.mjs +132 -29
- package/hub/routing/index.mjs +17 -12
- package/hub/routing/q-learning.mjs +76 -28
- package/hub/server.mjs +4 -4
- package/hub/session-fingerprint.mjs +126 -60
- package/hub/state.mjs +84 -43
- package/hub/store-adapter.mjs +59 -26
- package/hub/store.mjs +356 -153
- package/hub/team/agent-map.json +22 -7
- package/hub/team/ansi.mjs +186 -122
- package/hub/team/backend.mjs +28 -10
- package/hub/team/cli/commands/attach.mjs +29 -9
- package/hub/team/cli/commands/control.mjs +29 -8
- package/hub/team/cli/commands/debug.mjs +32 -11
- package/hub/team/cli/commands/focus.mjs +38 -11
- package/hub/team/cli/commands/interrupt.mjs +18 -6
- package/hub/team/cli/commands/kill.mjs +16 -5
- package/hub/team/cli/commands/list.mjs +11 -4
- package/hub/team/cli/commands/send.mjs +19 -6
- package/hub/team/cli/commands/start/index.mjs +154 -31
- package/hub/team/cli/commands/start/parse-args.mjs +38 -11
- package/hub/team/cli/commands/start/start-headless.mjs +112 -36
- package/hub/team/cli/commands/start/start-in-process.mjs +12 -2
- package/hub/team/cli/commands/start/start-mux.mjs +70 -21
- package/hub/team/cli/commands/start/start-wt.mjs +29 -12
- package/hub/team/cli/commands/status.mjs +43 -14
- package/hub/team/cli/commands/stop.mjs +11 -4
- package/hub/team/cli/commands/task.mjs +8 -3
- package/hub/team/cli/commands/tasks.mjs +1 -1
- package/hub/team/cli/index.mjs +2 -2
- package/hub/team/cli/manifest.mjs +38 -8
- package/hub/team/cli/render.mjs +30 -8
- package/hub/team/cli/services/attach-fallback.mjs +31 -11
- package/hub/team/cli/services/hub-client.mjs +42 -14
- package/hub/team/cli/services/member-selector.mjs +11 -4
- package/hub/team/cli/services/native-control.mjs +48 -21
- package/hub/team/cli/services/runtime-mode.mjs +2 -1
- package/hub/team/cli/services/state-store.mjs +25 -8
- package/hub/team/cli/services/task-model.mjs +16 -6
- package/hub/team/conductor-mesh-bridge.mjs +24 -23
- package/hub/team/conductor.mjs +8 -4
- package/hub/team/dashboard-anchor.mjs +4 -5
- package/hub/team/dashboard-layout.mjs +3 -1
- package/hub/team/dashboard-open.mjs +41 -21
- package/hub/team/dashboard.mjs +76 -28
- package/hub/team/event-log.mjs +18 -10
- package/hub/team/handoff.mjs +31 -15
- package/hub/team/headless.mjs +2 -1
- package/hub/team/health-probe.mjs +69 -54
- package/hub/team/launcher-template.mjs +16 -13
- package/hub/team/native-supervisor.mjs +65 -21
- package/hub/team/native.mjs +74 -35
- package/hub/team/nativeProxy.mjs +184 -113
- package/hub/team/notify.mjs +119 -76
- package/hub/team/orchestrator.mjs +9 -4
- package/hub/team/pane.mjs +12 -7
- package/hub/team/process-cleanup.mjs +25 -16
- package/hub/team/psmux.mjs +491 -201
- package/hub/team/remote-probe.mjs +68 -52
- package/hub/team/remote-session.mjs +117 -59
- package/hub/team/remote-watcher.mjs +61 -33
- package/hub/team/routing.mjs +51 -25
- package/hub/team/runtime-strategy.mjs +3 -1
- package/hub/team/session.mjs +98 -34
- package/hub/team/staleState.mjs +72 -30
- package/hub/team/swarm-locks.mjs +15 -13
- package/hub/team/swarm-planner.mjs +32 -21
- package/hub/team/swarm-reconciler.mjs +48 -23
- package/hub/team/tui-lite.mjs +266 -68
- package/hub/team/tui-remote-adapter.mjs +14 -10
- package/hub/team/tui-viewer.mjs +99 -43
- package/hub/team/tui.mjs +708 -271
- package/hub/team/worktree-lifecycle.mjs +152 -58
- package/hub/team/wt-manager.mjs +24 -14
- package/hub/token-mode.mjs +71 -71
- package/hub/tray.mjs +66 -23
- package/hub/workers/claude-worker.mjs +162 -118
- package/hub/workers/codex-mcp.mjs +192 -141
- package/hub/workers/delegator-mcp.mjs +507 -333
- package/hub/workers/factory.mjs +8 -8
- package/hub/workers/gemini-worker.mjs +115 -84
- package/hub/workers/interface.mjs +6 -1
- package/hub/workers/worker-utils.mjs +21 -14
- package/hud/colors.mjs +27 -9
- package/hud/constants.mjs +162 -26
- package/hud/context-monitor.mjs +82 -41
- package/hud/hud-qos-status.mjs +129 -49
- package/hud/mission-board.mjs +6 -3
- package/hud/providers/claude.mjs +226 -115
- package/hud/providers/codex.mjs +62 -22
- package/hud/providers/gemini.mjs +168 -56
- package/hud/renderers.mjs +384 -119
- package/hud/terminal.mjs +101 -31
- package/hud/utils.mjs +78 -38
- package/mesh/index.mjs +11 -5
- package/mesh/mesh-budget.mjs +18 -9
- package/mesh/mesh-heartbeat.mjs +1 -1
- package/mesh/mesh-queue.mjs +3 -5
- package/mesh/mesh-router.mjs +5 -4
- package/package.json +2 -1
- package/scripts/__tests__/gen-skill-docs.test.mjs +36 -7
- package/scripts/__tests__/keyword-detector.test.mjs +77 -28
- package/scripts/__tests__/mcp-guard-engine.test.mjs +58 -20
- package/scripts/__tests__/remote-spawn-transfer.test.mjs +30 -19
- package/scripts/__tests__/remote-spawn.test.mjs +10 -4
- package/scripts/__tests__/session-start-fast.test.mjs +36 -0
- package/scripts/__tests__/skill-template.test.mjs +98 -50
- package/scripts/__tests__/smoke.test.mjs +1 -1
- package/scripts/__tests__/spawn-trace.test.mjs +102 -0
- package/scripts/__tests__/tfx-doctor-diagnose.test.mjs +48 -0
- package/scripts/cache-doctor.mjs +11 -4
- package/scripts/cache-warmup.mjs +96 -37
- package/scripts/claudemd-sync.mjs +27 -17
- package/scripts/codex-gateway-preflight.mjs +52 -37
- package/scripts/codex-mcp-gateway-sync.mjs +59 -39
- package/scripts/completions/tfx.bash +47 -47
- package/scripts/completions/tfx.fish +44 -44
- package/scripts/completions/tfx.zsh +83 -83
- package/scripts/config-audit.mjs +232 -0
- package/scripts/convert-to-tmpl.mjs +54 -0
- package/scripts/cross-review-gate.mjs +35 -12
- package/scripts/cross-review-tracker.mjs +21 -8
- package/scripts/demo.mjs +35 -17
- package/scripts/doctor-diagnose.mjs +284 -0
- package/scripts/gen-skill-docs.mjs +7 -2
- package/scripts/gen-skill-manifest.mjs +2 -1
- package/scripts/headless-guard.mjs +86 -48
- package/scripts/hub-ensure.mjs +45 -26
- package/scripts/keyword-detector.mjs +41 -20
- package/scripts/keyword-rules-expander.mjs +47 -30
- package/scripts/lib/claudemd-scanner.mjs +6 -1
- package/scripts/lib/context.mjs +3 -3
- package/scripts/lib/cross-review-utils.mjs +6 -3
- package/scripts/lib/env-probe.mjs +47 -28
- package/scripts/lib/gemini-profiles.mjs +44 -10
- package/scripts/lib/handoff.mjs +33 -17
- package/scripts/lib/hook-utils.mjs +8 -6
- package/scripts/lib/keyword-rules.mjs +43 -19
- package/scripts/lib/logger.mjs +24 -24
- package/scripts/lib/mcp-filter.mjs +377 -239
- package/scripts/lib/mcp-guard-engine.mjs +194 -79
- package/scripts/lib/mcp-manifest.mjs +23 -13
- package/scripts/lib/mcp-server-catalog.mjs +300 -63
- package/scripts/lib/psmux-info.mjs +11 -6
- package/scripts/lib/remote-spawn-transfer.mjs +44 -14
- package/scripts/lib/skill-template.mjs +30 -7
- package/scripts/mcp-check.mjs +58 -39
- package/scripts/mcp-gateway-config.mjs +83 -39
- package/scripts/mcp-gateway-ensure.mjs +43 -35
- package/scripts/mcp-gateway-integration-test.mjs +70 -58
- package/scripts/mcp-gateway-start.mjs +126 -60
- package/scripts/mcp-gateway-verify.mjs +24 -22
- package/scripts/mcp-safety-guard.mjs +44 -11
- package/scripts/notion-read.mjs +199 -84
- package/scripts/pack.mjs +94 -89
- package/scripts/preflight-cache.mjs +27 -10
- package/scripts/preinstall.mjs +42 -13
- package/scripts/remote-spawn.mjs +309 -94
- package/scripts/run.cjs +8 -5
- package/scripts/session-spawn-helper.mjs +130 -39
- package/scripts/session-stale-cleanup.mjs +123 -0
- package/scripts/setup.mjs +941 -492
- package/scripts/test-lock.mjs +20 -7
- package/scripts/test-tfx-route-no-claude-native.mjs +16 -12
- package/scripts/tfx-batch-stats.mjs +32 -11
- package/scripts/tfx-gate-activate.mjs +11 -4
- package/scripts/tfx-route-post.mjs +87 -20
- package/scripts/tfx-route-worker.mjs +57 -51
- package/scripts/tfx-route.sh +41 -124
- package/scripts/tmp-cleanup.mjs +21 -7
- package/scripts/token-snapshot.mjs +204 -85
- package/skills/.omc/state/agent-replay-8f0e10a9-9693-4410-96f5-a6b07e8ed995.jsonl +1 -0
- package/skills/.omc/state/idle-notif-cooldown.json +3 -0
- package/skills/.omc/state/last-tool-error.json +7 -0
- package/skills/.omc/state/subagent-tracking.json +7 -0
- package/skills/_templates/base.md +1 -6
- package/skills/merge-worktree/SKILL.md.tmpl +144 -0
- package/skills/shared/telemetry-segment.md +6 -0
- package/skills/star-prompt/SKILL.md.tmpl +222 -0
- package/skills/tfx-analysis/SKILL.md.tmpl +107 -0
- package/skills/tfx-analysis/skill.json +1 -6
- package/skills/tfx-auto/SKILL.md +1 -0
- package/skills/tfx-auto-codex/SKILL.md.tmpl +106 -0
- package/skills/tfx-auto-codex/skill.json +1 -3
- package/skills/tfx-autopilot/SKILL.md.tmpl +116 -0
- package/skills/tfx-autopilot/skill.json +1 -5
- package/skills/tfx-autoresearch/SKILL.md.tmpl +136 -0
- package/skills/tfx-autoroute/SKILL.md.tmpl +189 -0
- package/skills/tfx-autoroute/skill.json +1 -7
- package/skills/tfx-codex/SKILL.md +1 -0
- package/skills/tfx-codex/skill.json +1 -3
- package/skills/tfx-codex-swarm/SKILL.md.tmpl +16 -0
- package/skills/tfx-codex-swarm/evals/evals.json +1 -1
- package/skills/tfx-codex-swarm/skill.json +1 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/benchmark.json +54 -12
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/with_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/full-swarm-all-prds/without_skill/grading.json +35 -7
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/with_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/implicit-swarm-no-keywords/without_skill/grading.json +25 -5
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/with_skill/grading.json +20 -4
- package/skills/tfx-codex-swarm-workspace/iteration-1/selective-spawn-with-override/without_skill/grading.json +16 -4
- package/skills/tfx-consensus/SKILL.md.tmpl +146 -0
- package/skills/tfx-debate/SKILL.md.tmpl +192 -0
- package/skills/tfx-debate/skill.json +1 -7
- package/skills/tfx-deep-analysis/SKILL.md.tmpl +228 -0
- package/skills/tfx-deep-analysis/skill.json +1 -5
- package/skills/tfx-deep-interview/SKILL.md.tmpl +203 -0
- package/skills/tfx-deep-plan/SKILL.md.tmpl +282 -0
- package/skills/tfx-deep-qa/SKILL.md.tmpl +165 -0
- package/skills/tfx-deep-qa/skill.json +1 -6
- package/skills/tfx-deep-research/SKILL.md.tmpl +217 -0
- package/skills/tfx-deep-review/SKILL.md.tmpl +179 -0
- package/skills/tfx-doctor/SKILL.md +21 -0
- package/skills/tfx-doctor/SKILL.md.tmpl +172 -0
- package/skills/tfx-doctor/skill.json +1 -3
- package/skills/tfx-find/SKILL.md +1 -0
- package/skills/tfx-forge/SKILL.md.tmpl +187 -0
- package/skills/tfx-fullcycle/SKILL.md.tmpl +286 -0
- package/skills/tfx-fullcycle/skill.json +1 -6
- package/skills/tfx-gemini/SKILL.md.tmpl +91 -0
- package/skills/tfx-gemini/skill.json +1 -3
- package/skills/tfx-hooks/SKILL.md.tmpl +216 -0
- package/skills/tfx-hooks/skill.json +1 -3
- package/skills/tfx-hub/SKILL.md.tmpl +212 -0
- package/skills/tfx-hub/skill.json +1 -3
- package/skills/tfx-index/SKILL.md +1 -0
- package/skills/tfx-index/skill.json +1 -6
- package/skills/tfx-interview/SKILL.md.tmpl +285 -0
- package/skills/tfx-multi/SKILL.md.tmpl +183 -0
- package/skills/tfx-multi/skill.json +1 -3
- package/skills/tfx-panel/SKILL.md.tmpl +189 -0
- package/skills/tfx-panel/skill.json +1 -7
- package/skills/tfx-persist/SKILL.md.tmpl +270 -0
- package/skills/tfx-persist/skill.json +1 -7
- package/skills/tfx-plan/SKILL.md +1 -0
- package/skills/tfx-plan/skill.json +1 -6
- package/skills/tfx-profile/SKILL.md.tmpl +239 -0
- package/skills/tfx-profile/skill.json +1 -3
- package/skills/tfx-prune/SKILL.md.tmpl +200 -0
- package/skills/tfx-prune/skill.json +1 -7
- package/skills/tfx-psmux-rules/SKILL.md.tmpl +326 -0
- package/skills/tfx-psmux-rules/skill.json +1 -4
- package/skills/tfx-qa/SKILL.md +1 -0
- package/skills/tfx-qa/skill.json +1 -6
- package/skills/tfx-ralph/SKILL.md.tmpl +28 -0
- package/skills/tfx-ralph/skill.json +1 -4
- package/skills/tfx-remote-setup/SKILL.md.tmpl +576 -0
- package/skills/tfx-remote-setup/skill.json +1 -3
- package/skills/tfx-remote-spawn/SKILL.md.tmpl +263 -0
- package/skills/tfx-remote-spawn/references/hosts.json +16 -0
- package/skills/tfx-remote-spawn/skill.json +1 -4
- package/skills/tfx-research/SKILL.md +1 -0
- package/skills/tfx-review/SKILL.md +1 -0
- package/skills/tfx-review/skill.json +1 -6
- package/skills/tfx-setup/SKILL.md.tmpl +504 -0
- package/skills/tfx-setup/skill.json +1 -3
- package/skills/tfx-swarm/SKILL.md +22 -0
- package/skills/tfx-swarm/SKILL.md.tmpl +218 -0
- package/tui/codex-profile.mjs +88 -33
- package/tui/core.mjs +45 -15
- package/tui/doctor.mjs +75 -28
- package/tui/gemini-profile.mjs +74 -29
- package/tui/monitor-data.mjs +8 -4
- package/tui/monitor.mjs +71 -27
- package/tui/setup.mjs +133 -42
|
@@ -1,44 +1,77 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
|
|
2
3
|
// mcp-safety-guard.mjs — Gemini stdio MCP 자동 감지 + 치환
|
|
3
4
|
// SessionStart 훅으로 실행. stdio MCP는 Windows에서 spawn EPERM → Gemini stall 유발.
|
|
4
5
|
// 감지 시 공통 guard engine으로 제거/치환하고 백업 파일 생성.
|
|
5
6
|
|
|
6
|
-
import { join } from "node:path";
|
|
7
7
|
import { homedir } from "node:os";
|
|
8
|
-
import {
|
|
8
|
+
import { join } from "node:path";
|
|
9
|
+
import {
|
|
10
|
+
loadRegistry,
|
|
11
|
+
remediate,
|
|
12
|
+
scanForStdioServers,
|
|
13
|
+
} from "./lib/mcp-guard-engine.mjs";
|
|
9
14
|
|
|
10
15
|
const GEMINI_SETTINGS = join(homedir(), ".gemini", "settings.json");
|
|
11
16
|
|
|
12
|
-
function run() {
|
|
17
|
+
export async function run(stdinData) {
|
|
18
|
+
void stdinData;
|
|
19
|
+
|
|
13
20
|
let registry;
|
|
14
21
|
try {
|
|
15
22
|
registry = loadRegistry();
|
|
16
23
|
} catch {
|
|
17
|
-
return;
|
|
24
|
+
return { code: 0, stdout: "", stderr: "" };
|
|
18
25
|
}
|
|
19
26
|
|
|
20
27
|
const stdioServers = scanForStdioServers(GEMINI_SETTINGS);
|
|
21
28
|
|
|
22
|
-
if (stdioServers.length === 0)
|
|
29
|
+
if (stdioServers.length === 0) {
|
|
30
|
+
return { code: 0, stdout: "", stderr: "" };
|
|
31
|
+
}
|
|
23
32
|
|
|
24
33
|
const result = remediate(GEMINI_SETTINGS, stdioServers, registry.policies);
|
|
25
34
|
const names = stdioServers.map((server) => server.name).join(", ");
|
|
35
|
+
const stdout = [];
|
|
26
36
|
|
|
27
37
|
if (result.modified) {
|
|
28
38
|
const actionLabel = result.replacement ? "자동 치환" : "자동 제거";
|
|
29
|
-
|
|
39
|
+
stdout.push(
|
|
40
|
+
`[mcp-safety] ${stdioServers.length}개 stdio MCP ${actionLabel}: ${names}`,
|
|
41
|
+
);
|
|
30
42
|
if (result.replacement?.name && result.replacement?.url) {
|
|
31
|
-
|
|
43
|
+
stdout.push(
|
|
44
|
+
`[mcp-safety] 대체 서버: ${result.replacement.name} -> ${result.replacement.url}`,
|
|
45
|
+
);
|
|
32
46
|
}
|
|
33
47
|
if (result.backupPath) {
|
|
34
|
-
|
|
48
|
+
stdout.push(`[mcp-safety] 백업: ${result.backupPath}`);
|
|
35
49
|
}
|
|
36
|
-
|
|
50
|
+
stdout.push(
|
|
51
|
+
"[mcp-safety] Gemini는 Hub URL만 사용합니다. stdio MCP는 spawn EPERM을 유발합니다.",
|
|
52
|
+
);
|
|
37
53
|
}
|
|
38
54
|
|
|
39
55
|
for (const warning of result.warnings || []) {
|
|
40
|
-
|
|
56
|
+
stdout.push(warning);
|
|
41
57
|
}
|
|
58
|
+
|
|
59
|
+
return {
|
|
60
|
+
code: 0,
|
|
61
|
+
stdout: stdout.length > 0 ? `${stdout.join("\n")}\n` : "",
|
|
62
|
+
stderr: "",
|
|
63
|
+
};
|
|
42
64
|
}
|
|
43
65
|
|
|
44
|
-
|
|
66
|
+
const isMain =
|
|
67
|
+
process.argv[1] &&
|
|
68
|
+
import.meta.url.endsWith(
|
|
69
|
+
process.argv[1].replace(/\\/g, "/").split("/").pop(),
|
|
70
|
+
);
|
|
71
|
+
|
|
72
|
+
if (isMain) {
|
|
73
|
+
const result = await run();
|
|
74
|
+
if (result.stdout) process.stdout.write(result.stdout);
|
|
75
|
+
if (result.stderr) process.stderr.write(result.stderr);
|
|
76
|
+
process.exit(result.code);
|
|
77
|
+
}
|
package/scripts/notion-read.mjs
CHANGED
|
@@ -17,28 +17,35 @@
|
|
|
17
17
|
// --guest notion-guest 통합 사용 (기본: notion)
|
|
18
18
|
// --delegate Claude 이관 모드 (notion-guest 우선, 파일 저장)
|
|
19
19
|
|
|
20
|
-
import { execSync } from
|
|
21
|
-
import {
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
20
|
+
import { execSync } from "child_process";
|
|
21
|
+
import {
|
|
22
|
+
appendFileSync,
|
|
23
|
+
existsSync,
|
|
24
|
+
mkdirSync,
|
|
25
|
+
readFileSync,
|
|
26
|
+
unlinkSync,
|
|
27
|
+
writeFileSync,
|
|
28
|
+
} from "fs";
|
|
29
|
+
import { homedir, tmpdir } from "os";
|
|
30
|
+
import { dirname, join, resolve } from "path";
|
|
31
|
+
|
|
32
|
+
import { buildExecArgs } from "../hub/codex-adapter.mjs";
|
|
33
|
+
|
|
34
|
+
const VERSION = "1.2";
|
|
35
|
+
const CLAUDE_DIR = join(homedir(), ".claude");
|
|
36
|
+
const MCP_CACHE = join(CLAUDE_DIR, "cache", "mcp-inventory.json");
|
|
37
|
+
const LOG_FILE = join(CLAUDE_DIR, "logs", "tfx-route-stats.jsonl");
|
|
38
|
+
const _ACC_FILE = join(CLAUDE_DIR, "cache", "sv-accumulator.json");
|
|
32
39
|
|
|
33
40
|
// ── ANSI 색상 ──
|
|
34
|
-
const AMBER =
|
|
35
|
-
const GREEN =
|
|
36
|
-
const RED =
|
|
37
|
-
const YELLOW =
|
|
38
|
-
const DIM =
|
|
39
|
-
const BOLD =
|
|
40
|
-
const RESET =
|
|
41
|
-
const GRAY =
|
|
41
|
+
const AMBER = "\x1b[38;5;214m";
|
|
42
|
+
const GREEN = "\x1b[38;5;82m";
|
|
43
|
+
const RED = "\x1b[38;5;196m";
|
|
44
|
+
const YELLOW = "\x1b[33m";
|
|
45
|
+
const DIM = "\x1b[2m";
|
|
46
|
+
const BOLD = "\x1b[1m";
|
|
47
|
+
const RESET = "\x1b[0m";
|
|
48
|
+
const GRAY = "\x1b[38;5;245m";
|
|
42
49
|
|
|
43
50
|
// ── URL 파싱 ──
|
|
44
51
|
function parseNotionUrl(input) {
|
|
@@ -47,7 +54,11 @@ function parseNotionUrl(input) {
|
|
|
47
54
|
return { pageId: input, blockId: null };
|
|
48
55
|
}
|
|
49
56
|
// UUID 형식
|
|
50
|
-
if (
|
|
57
|
+
if (
|
|
58
|
+
/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i.test(
|
|
59
|
+
input,
|
|
60
|
+
)
|
|
61
|
+
) {
|
|
51
62
|
return { pageId: input.replace(/-/g, ""), blockId: null };
|
|
52
63
|
}
|
|
53
64
|
// URL에서 page_id + optional #block_id 추출
|
|
@@ -70,12 +81,16 @@ function getNotionMcpClis(useGuest) {
|
|
|
70
81
|
|
|
71
82
|
if (inv.codex?.servers) {
|
|
72
83
|
result.codex = inv.codex.servers.some(
|
|
73
|
-
(s) =>
|
|
84
|
+
(s) =>
|
|
85
|
+
s.name === serverName &&
|
|
86
|
+
(s.status === "enabled" || s.status === "configured"),
|
|
74
87
|
);
|
|
75
88
|
}
|
|
76
89
|
if (inv.gemini?.servers) {
|
|
77
90
|
result.gemini = inv.gemini.servers.some(
|
|
78
|
-
(s) =>
|
|
91
|
+
(s) =>
|
|
92
|
+
s.name === serverName &&
|
|
93
|
+
(s.status === "enabled" || s.status === "configured"),
|
|
79
94
|
);
|
|
80
95
|
}
|
|
81
96
|
} catch {}
|
|
@@ -86,8 +101,16 @@ function getNotionMcpClis(useGuest) {
|
|
|
86
101
|
// ── CLI 존재 확인 ──
|
|
87
102
|
function cliExists(name) {
|
|
88
103
|
try {
|
|
89
|
-
const cmd =
|
|
90
|
-
|
|
104
|
+
const cmd =
|
|
105
|
+
process.platform === "win32"
|
|
106
|
+
? `where ${name} 2>nul`
|
|
107
|
+
: `which ${name} 2>/dev/null`;
|
|
108
|
+
const result = execSync(cmd, {
|
|
109
|
+
encoding: "utf8",
|
|
110
|
+
timeout: 5000,
|
|
111
|
+
stdio: ["pipe", "pipe", "ignore"],
|
|
112
|
+
windowsHide: true,
|
|
113
|
+
});
|
|
91
114
|
return !!result.trim();
|
|
92
115
|
} catch {
|
|
93
116
|
return false;
|
|
@@ -127,14 +150,18 @@ ${mcpServer} MCP 서버의 도구를 사용하라.
|
|
|
127
150
|
각 블록의 has_children이 true이면, 해당 block_id로 블록 자식 조회를 재귀 호출.
|
|
128
151
|
최대 깊이: ${depth}단계. 깊이 초과 시 "[깊이 초과]" 표시.
|
|
129
152
|
|
|
130
|
-
### 4단계: 댓글 수집${
|
|
153
|
+
### 4단계: 댓글 수집${
|
|
154
|
+
includeComments
|
|
155
|
+
? `
|
|
131
156
|
페이지 및 블록 댓글을 수집하라.
|
|
132
157
|
- 댓글 조회 도구를 호출하라 (block_id: "${pageId}")로 페이지 전체 댓글을 가져와라.
|
|
133
158
|
- 응답의 has_more가 true이면 next_cursor로 반복.
|
|
134
159
|
- 각 댓글의 parent.type이 "block_id"이면 해당 블록의 인라인 댓글이다.
|
|
135
160
|
- parent.type이 "page_id"이면 페이지 레벨 토론 댓글이다.
|
|
136
|
-
- 404 에러 발생 시 댓글 권한이 없는 것이므로 건너뛰어라.`
|
|
137
|
-
|
|
161
|
+
- 404 에러 발생 시 댓글 권한이 없는 것이므로 건너뛰어라.`
|
|
162
|
+
: `
|
|
163
|
+
댓글 수집을 건너뛴다 (--comments 플래그 미지정).`
|
|
164
|
+
}
|
|
138
165
|
|
|
139
166
|
### 5단계: 마크다운 변환
|
|
140
167
|
- heading_1/2/3 → #/##/###
|
|
@@ -161,51 +188,63 @@ ${mcpServer} MCP 서버의 도구를 사용하라.
|
|
|
161
188
|
- 모든 블록을 빠짐없이 순서대로 출력
|
|
162
189
|
- 읽기 실패 블록은 <!-- 읽기 실패: block_id --> 주석 남기기
|
|
163
190
|
- rich_text의 annotations (bold, italic, code, strikethrough) 반영
|
|
164
|
-
- 링크는 [텍스트](url) 형식${
|
|
191
|
+
- 링크는 [텍스트](url) 형식${
|
|
192
|
+
includeComments
|
|
193
|
+
? `
|
|
165
194
|
- 블록 인라인 댓글: 해당 블록 바로 아래에 > **[댓글]** @작성자: 내용 형식으로 삽입
|
|
166
195
|
- 페이지 토론 댓글: 문서 맨 끝에 ## 토론 섹션으로 모아서 출력
|
|
167
|
-
- 댓글의 rich_text도 마크다운으로 변환`
|
|
196
|
+
- 댓글의 rich_text도 마크다운으로 변환`
|
|
197
|
+
: ""
|
|
198
|
+
}
|
|
168
199
|
- 최종 결과만 출력 — 중간 과정 설명 불필요`;
|
|
169
200
|
}
|
|
170
201
|
|
|
171
202
|
// ── CLI 실행 (임시 파일 + execSync — Windows .cmd 호환) ──
|
|
172
|
-
function runWithCli(cliType, prompt, timeout, runMode =
|
|
173
|
-
const cliName =
|
|
203
|
+
function runWithCli(cliType, prompt, timeout, runMode = "fg") {
|
|
204
|
+
const cliName =
|
|
205
|
+
cliType === "claude" ? "claude" : cliType === "codex" ? "codex" : "gemini";
|
|
174
206
|
if (!cliExists(cliName)) {
|
|
175
|
-
return {
|
|
207
|
+
return {
|
|
208
|
+
success: false,
|
|
209
|
+
output: "",
|
|
210
|
+
error: `${cliType} CLI 미설치`,
|
|
211
|
+
cli: cliType,
|
|
212
|
+
};
|
|
176
213
|
}
|
|
177
214
|
|
|
178
215
|
// 프롬프트를 임시 파일에 저장 (shell escaping 회피)
|
|
179
216
|
const promptFile = join(tmpdir(), `notion-prompt-${Date.now()}.md`);
|
|
180
|
-
writeFileSync(promptFile, prompt,
|
|
181
|
-
const promptPath = promptFile.replace(/\\/g,
|
|
217
|
+
writeFileSync(promptFile, prompt, "utf8");
|
|
218
|
+
const promptPath = promptFile.replace(/\\/g, "/");
|
|
182
219
|
|
|
183
220
|
// CLI에 전달할 짧은 메타 프롬프트
|
|
184
221
|
const metaPrompt = `Read the file at ${promptPath} and execute all instructions in it exactly as described. Output only the final markdown result.`;
|
|
185
222
|
|
|
186
223
|
let cmd;
|
|
187
|
-
if (cliType ===
|
|
224
|
+
if (cliType === "codex") {
|
|
188
225
|
cmd = buildExecArgs({ prompt: metaPrompt });
|
|
189
|
-
} else if (cliType ===
|
|
226
|
+
} else if (cliType === "gemini") {
|
|
190
227
|
cmd = `gemini -m gemini-3-flash-preview -y --allowed-mcp-server-names notion,notion-guest --prompt "${metaPrompt}"`;
|
|
191
228
|
} else {
|
|
192
229
|
// Claude CLI — print 모드 (MCP 도구 자동 접근)
|
|
193
230
|
cmd = `claude -p "${metaPrompt}"`;
|
|
194
231
|
}
|
|
195
232
|
|
|
196
|
-
console.error(
|
|
233
|
+
console.error(
|
|
234
|
+
`${AMBER}▸${RESET} ${cliType}로 실행 중... (timeout: ${timeout}s)`,
|
|
235
|
+
);
|
|
197
236
|
const startTime = Date.now();
|
|
198
237
|
|
|
199
|
-
let stdout =
|
|
200
|
-
let stderr =
|
|
238
|
+
let stdout = "";
|
|
239
|
+
let stderr = "";
|
|
201
240
|
let exitCode = 0;
|
|
202
241
|
|
|
203
242
|
try {
|
|
204
243
|
stdout = execSync(cmd, {
|
|
205
|
-
encoding:
|
|
244
|
+
encoding: "utf8",
|
|
206
245
|
timeout: (timeout + 30) * 1000,
|
|
207
246
|
maxBuffer: 10 * 1024 * 1024,
|
|
208
|
-
stdio: [
|
|
247
|
+
stdio: ["pipe", "pipe", "pipe"],
|
|
209
248
|
cwd: process.cwd(),
|
|
210
249
|
windowsHide: true,
|
|
211
250
|
});
|
|
@@ -218,7 +257,9 @@ function runWithCli(cliType, prompt, timeout, runMode = 'fg') {
|
|
|
218
257
|
const elapsed = Math.round((Date.now() - startTime) / 1000);
|
|
219
258
|
|
|
220
259
|
// 임시 파일 정리
|
|
221
|
-
try {
|
|
260
|
+
try {
|
|
261
|
+
unlinkSync(promptFile);
|
|
262
|
+
} catch {}
|
|
222
263
|
|
|
223
264
|
// 실행 로그 기록
|
|
224
265
|
logExecution(cliType, exitCode, elapsed, timeout, stderr, runMode);
|
|
@@ -268,30 +309,43 @@ function cleanCodexOutput(raw) {
|
|
|
268
309
|
}
|
|
269
310
|
|
|
270
311
|
// ── 실행 로그 (tfx-route.sh 호환) ──
|
|
271
|
-
function logExecution(
|
|
312
|
+
function logExecution(
|
|
313
|
+
cliType,
|
|
314
|
+
exitCode,
|
|
315
|
+
elapsed,
|
|
316
|
+
timeout,
|
|
317
|
+
stderr,
|
|
318
|
+
runMode = "fg",
|
|
319
|
+
) {
|
|
272
320
|
try {
|
|
273
321
|
const logDir = dirname(LOG_FILE);
|
|
274
322
|
if (!existsSync(logDir)) mkdirSync(logDir, { recursive: true });
|
|
275
323
|
|
|
276
324
|
const ts = new Date().toISOString();
|
|
277
|
-
const status =
|
|
325
|
+
const status =
|
|
326
|
+
exitCode === 0 ? "success" : exitCode === 124 ? "timeout" : "failed";
|
|
278
327
|
const entry = JSON.stringify({
|
|
279
328
|
ts,
|
|
280
|
-
agent:
|
|
329
|
+
agent: "notion-read",
|
|
281
330
|
cli: cliType,
|
|
282
|
-
effort:
|
|
331
|
+
effort:
|
|
332
|
+
cliType === "codex"
|
|
333
|
+
? "high"
|
|
334
|
+
: cliType === "claude"
|
|
335
|
+
? "sonnet"
|
|
336
|
+
: "flash",
|
|
283
337
|
run_mode: runMode,
|
|
284
|
-
opus_oversight:
|
|
338
|
+
opus_oversight: "false",
|
|
285
339
|
status,
|
|
286
340
|
exit_code: exitCode,
|
|
287
341
|
elapsed_sec: elapsed,
|
|
288
342
|
timeout_sec: timeout,
|
|
289
|
-
mcp_profile: runMode ===
|
|
343
|
+
mcp_profile: runMode === "delegate" ? "notion-guest" : "notion",
|
|
290
344
|
input_tokens: 0,
|
|
291
345
|
output_tokens: 0,
|
|
292
346
|
total_tokens: 0,
|
|
293
347
|
});
|
|
294
|
-
appendFileSync(LOG_FILE, entry +
|
|
348
|
+
appendFileSync(LOG_FILE, entry + "\n");
|
|
295
349
|
} catch {}
|
|
296
350
|
}
|
|
297
351
|
|
|
@@ -364,7 +418,7 @@ function main() {
|
|
|
364
418
|
case "--comments":
|
|
365
419
|
includeComments = true;
|
|
366
420
|
break;
|
|
367
|
-
case
|
|
421
|
+
case "--delegate":
|
|
368
422
|
delegateMode = true;
|
|
369
423
|
break;
|
|
370
424
|
}
|
|
@@ -380,20 +434,35 @@ function main() {
|
|
|
380
434
|
console.error(
|
|
381
435
|
`${AMBER}▸${RESET} 페이지: ${parsed.pageId}${parsed.blockId ? ` (블록: ${parsed.blockId})` : ""}`,
|
|
382
436
|
);
|
|
383
|
-
console.error(
|
|
437
|
+
console.error(
|
|
438
|
+
`${GRAY} 통합: ${delegateMode ? "notion-guest(우선)" : useGuest ? "notion-guest" : "notion"} | 깊이: ${depth} | 댓글: ${includeComments ? "O" : "X"} | 타임아웃: ${timeout}s${RESET}`,
|
|
439
|
+
);
|
|
384
440
|
|
|
385
441
|
// 프롬프트 생성
|
|
386
|
-
const prompt = buildPrompt(
|
|
442
|
+
const prompt = buildPrompt(
|
|
443
|
+
parsed.pageId,
|
|
444
|
+
parsed.blockId,
|
|
445
|
+
depth,
|
|
446
|
+
useGuest,
|
|
447
|
+
includeComments,
|
|
448
|
+
);
|
|
387
449
|
// Claude 폴백용: notion/notion-guest 양쪽 시도 프롬프트
|
|
388
|
-
const claudePrompt = buildPrompt(
|
|
389
|
-
.
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
450
|
+
const claudePrompt = buildPrompt(
|
|
451
|
+
parsed.pageId,
|
|
452
|
+
parsed.blockId,
|
|
453
|
+
depth,
|
|
454
|
+
false,
|
|
455
|
+
includeComments,
|
|
456
|
+
).replace(
|
|
457
|
+
"notion MCP 서버의 도구를 사용하라.",
|
|
458
|
+
"가능하면 notion-guest MCP 서버를 먼저 사용하라. 실패하면 notion MCP 서버를 사용하라.",
|
|
459
|
+
);
|
|
393
460
|
|
|
394
461
|
// delegate 모드: Claude 단독 + notion-guest 우선 + 파일 저장
|
|
395
462
|
if (delegateMode) {
|
|
396
|
-
console.error(
|
|
463
|
+
console.error(
|
|
464
|
+
`${AMBER}▸${RESET} delegate 모드 활성화: Claude로 notion-guest 우선 접근`,
|
|
465
|
+
);
|
|
397
466
|
|
|
398
467
|
const delegatePrompt = `${claudePrompt}
|
|
399
468
|
|
|
@@ -402,34 +471,54 @@ function main() {
|
|
|
402
471
|
- notion-guest가 실패하거나 미구성일 때만 notion 서버로 폴백하라.
|
|
403
472
|
- 도구 호출 결과를 바탕으로 최종 마크다운만 출력하라.`;
|
|
404
473
|
|
|
405
|
-
const delegateResult = runWithCli(
|
|
474
|
+
const delegateResult = runWithCli(
|
|
475
|
+
"claude",
|
|
476
|
+
delegatePrompt,
|
|
477
|
+
timeout,
|
|
478
|
+
"delegate",
|
|
479
|
+
);
|
|
406
480
|
if (!delegateResult.success) {
|
|
407
|
-
console.error(
|
|
481
|
+
console.error(
|
|
482
|
+
`${RED}✗${RESET} delegate 모드 실패: ${delegateResult.error}`,
|
|
483
|
+
);
|
|
408
484
|
if (delegateResult.stderr) {
|
|
409
|
-
console.error(
|
|
485
|
+
console.error(
|
|
486
|
+
`${GRAY} stderr: ${delegateResult.stderr.slice(0, 250)}${RESET}`,
|
|
487
|
+
);
|
|
410
488
|
}
|
|
411
|
-
console.error(
|
|
412
|
-
|
|
489
|
+
console.error(
|
|
490
|
+
`${GRAY} 대안: --delegate 없이 실행해 기존 폴백 체인을 사용하세요.${RESET}`,
|
|
491
|
+
);
|
|
492
|
+
console.error(
|
|
493
|
+
`${GRAY} 예: tfx notion-read ${parsed.pageId} --comments${RESET}`,
|
|
494
|
+
);
|
|
413
495
|
process.exit(1);
|
|
414
496
|
}
|
|
415
497
|
|
|
416
498
|
const delegateOutput = delegateResult.output.trim();
|
|
417
499
|
const isDelegateFailureOutput =
|
|
418
|
-
(delegateOutput.includes(
|
|
500
|
+
(delegateOutput.includes("조회 실패") ||
|
|
501
|
+
delegateOutput.includes("읽기 실패") ||
|
|
502
|
+
delegateOutput.includes("not_found")) &&
|
|
419
503
|
delegateOutput.length < 500;
|
|
420
504
|
|
|
421
505
|
if (delegateOutput.length <= 100 || isDelegateFailureOutput) {
|
|
422
|
-
console.error(
|
|
423
|
-
|
|
506
|
+
console.error(
|
|
507
|
+
`${RED}✗${RESET} delegate 모드 실패: Claude 결과가 비정상적입니다.`,
|
|
508
|
+
);
|
|
509
|
+
console.error(
|
|
510
|
+
`${GRAY} 대안: --delegate 없이 실행해 Codex/Gemini/Claude 폴백 체인을 사용하세요.${RESET}`,
|
|
511
|
+
);
|
|
424
512
|
process.exit(1);
|
|
425
513
|
}
|
|
426
514
|
|
|
427
|
-
const delegateTarget =
|
|
515
|
+
const delegateTarget =
|
|
516
|
+
outputFile || join(".notion-cache", `${parsed.pageId}.md`);
|
|
428
517
|
const delegateDir = dirname(delegateTarget);
|
|
429
|
-
if (delegateDir && delegateDir !==
|
|
518
|
+
if (delegateDir && delegateDir !== "." && !existsSync(delegateDir)) {
|
|
430
519
|
mkdirSync(delegateDir, { recursive: true });
|
|
431
520
|
}
|
|
432
|
-
writeFileSync(delegateTarget, delegateOutput,
|
|
521
|
+
writeFileSync(delegateTarget, delegateOutput, "utf8");
|
|
433
522
|
|
|
434
523
|
const savedPath = resolve(delegateTarget);
|
|
435
524
|
console.error(`${GREEN}✓${RESET} delegate 결과 저장: ${savedPath}`);
|
|
@@ -447,12 +536,18 @@ function main() {
|
|
|
447
536
|
if (!mcpAvail.codex && !mcpAvail.gemini) {
|
|
448
537
|
console.error(`${YELLOW}!${RESET} Codex/Gemini에 Notion MCP 미설치.`);
|
|
449
538
|
console.error(`${GRAY} Codex: codex mcp add notion${RESET}`);
|
|
450
|
-
console.error(
|
|
539
|
+
console.error(
|
|
540
|
+
`${GRAY} Gemini: ~/.gemini/settings.json에 notion 서버 추가${RESET}`,
|
|
541
|
+
);
|
|
451
542
|
console.error(`${GRAY} 설치 후 tfx doctor --reset으로 캐시 갱신${RESET}`);
|
|
452
543
|
} else if (!mcpAvail.codex) {
|
|
453
|
-
console.error(
|
|
544
|
+
console.error(
|
|
545
|
+
`${GRAY} Codex에 Notion MCP 미설치: codex mcp add notion${RESET}`,
|
|
546
|
+
);
|
|
454
547
|
} else if (!mcpAvail.gemini) {
|
|
455
|
-
console.error(
|
|
548
|
+
console.error(
|
|
549
|
+
`${GRAY} Gemini에 Notion MCP 미설치: ~/.gemini/settings.json 확인${RESET}`,
|
|
550
|
+
);
|
|
456
551
|
}
|
|
457
552
|
|
|
458
553
|
// CLI 실행 순서 결정 (Codex → Gemini → Claude)
|
|
@@ -478,16 +573,21 @@ function main() {
|
|
|
478
573
|
|
|
479
574
|
if (result.success) {
|
|
480
575
|
// Codex JSON-line 출력 정리
|
|
481
|
-
let output =
|
|
576
|
+
let output =
|
|
577
|
+
cli === "codex" ? cleanCodexOutput(result.output) : result.output;
|
|
482
578
|
output = output.trim();
|
|
483
579
|
|
|
484
580
|
// 실패 마커 감지 (404, 접근 실패 등)
|
|
485
581
|
const isFailureOutput =
|
|
486
|
-
(output.includes("조회 실패") ||
|
|
582
|
+
(output.includes("조회 실패") ||
|
|
583
|
+
output.includes("읽기 실패") ||
|
|
584
|
+
output.includes("not_found")) &&
|
|
487
585
|
output.length < 500;
|
|
488
586
|
|
|
489
587
|
if (output.length > 100 && !isFailureOutput) {
|
|
490
|
-
console.error(
|
|
588
|
+
console.error(
|
|
589
|
+
`${GREEN}✓${RESET} ${cli}로 성공 (${output.length} chars, ${result.elapsed}s)`,
|
|
590
|
+
);
|
|
491
591
|
|
|
492
592
|
if (outputFile) {
|
|
493
593
|
const outDir = dirname(outputFile);
|
|
@@ -505,17 +605,25 @@ function main() {
|
|
|
505
605
|
// 404 접근 실패 감지
|
|
506
606
|
if (isFailureOutput) {
|
|
507
607
|
notionAccessFailed = true;
|
|
508
|
-
console.error(
|
|
608
|
+
console.error(
|
|
609
|
+
`${YELLOW}!${RESET} ${cli}: Notion 접근 실패 (404) — 폴백`,
|
|
610
|
+
);
|
|
509
611
|
if (!useGuest && cli !== "claude") {
|
|
510
|
-
console.error(
|
|
612
|
+
console.error(
|
|
613
|
+
`${GRAY} --guest 플래그로 notion-guest 통합 시도 가능${RESET}`,
|
|
614
|
+
);
|
|
511
615
|
}
|
|
512
616
|
} else {
|
|
513
|
-
console.error(
|
|
617
|
+
console.error(
|
|
618
|
+
`${YELLOW}!${RESET} ${cli}: 출력 부족 (${output.length} chars) — 폴백`,
|
|
619
|
+
);
|
|
514
620
|
}
|
|
515
621
|
} else {
|
|
516
622
|
console.error(`${YELLOW}!${RESET} ${cli} 실패: ${result.error}`);
|
|
517
623
|
if (result.stderr) {
|
|
518
|
-
console.error(
|
|
624
|
+
console.error(
|
|
625
|
+
`${GRAY} stderr: ${result.stderr.slice(0, 200)}${RESET}`,
|
|
626
|
+
);
|
|
519
627
|
}
|
|
520
628
|
}
|
|
521
629
|
|
|
@@ -531,16 +639,23 @@ function main() {
|
|
|
531
639
|
console.error(`${RED}✗${RESET} 모든 CLI 실패`);
|
|
532
640
|
if (notionAccessFailed) {
|
|
533
641
|
console.error(`${YELLOW}!${RESET} Notion 페이지 접근 권한 문제.`);
|
|
534
|
-
console.error(
|
|
535
|
-
|
|
536
|
-
|
|
642
|
+
console.error(
|
|
643
|
+
`${GRAY} 1. Notion에서 페이지 → ... → 연결(Connections)에서 통합 추가${RESET}`,
|
|
644
|
+
);
|
|
645
|
+
console.error(
|
|
646
|
+
`${GRAY} 2. --guest 플래그로 notion-guest 통합 시도${RESET}`,
|
|
647
|
+
);
|
|
648
|
+
console.error(
|
|
649
|
+
`${GRAY} 3. --cli claude로 Claude 직접 사용 (Claude에 접근 권한 있는 경우)${RESET}`,
|
|
650
|
+
);
|
|
537
651
|
}
|
|
538
652
|
|
|
539
653
|
// 부분 결과라도 출력
|
|
540
654
|
if (lastResult?.output) {
|
|
541
655
|
const partial = lastResult.output.trim();
|
|
542
656
|
if (partial.length > 10) {
|
|
543
|
-
const cleaned =
|
|
657
|
+
const cleaned =
|
|
658
|
+
lastResult.cli === "codex" ? cleanCodexOutput(partial) : partial;
|
|
544
659
|
if (outputFile) {
|
|
545
660
|
writeFileSync(outputFile, cleaned, "utf8");
|
|
546
661
|
console.error(`${YELLOW}!${RESET} 부분 결과 저장: ${outputFile}`);
|