feat(fb-31): single-file / stdin ingest — agent on-demand 저장 #111

Merged
altair823 merged 16 commits from feat/p9-fb-31-single-file-stdin-ingest into main 2026-05-07 09:56:11 +00:00

16 Commits

Author SHA1 Message Date
th-kim0823
47bfd518c8 📝 docs: comprehensive MCP usage guide (fb-31)
신규 docs/mcp-usage.md (~280 line) — agent integration 의 종합 가이드:

- Quick start + `--config` thread 예시
- Host config 예시 (Claude Code / Cursor / OpenAI Agents / Copilot CLI)
- 6 tool catalog (search / ask / schema / doctor / ingest_file / ingest_stdin)
  각 tool 의 input shape, defaults, output 예시, "언제 사용", mutation
  주의사항.
- Troubleshooting — error.v1 의 7 code 별 조치 표 + grounded:false +
  doctor !ok + empty search + tool-not-found 시나리오.
- Multi-turn ask + session 관리 — session_id 명명, 새 session 시작
  시점, lifetime, single-shot vs session 비교.
- Performance / Security 절.

README.md 의 기존 MCP 절은 quick start 만 유지하고 docs/mcp-usage.md
링크. integrations/claude-code/kebab/SKILL.md 도 동일 cross-link.

agent 사용자 도그푸딩 후속 의견 — host-agnostic 가이드 + 명시적
troubleshooting 표 + multi-turn session 명명 컨벤션 부재 해소.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:53:59 +09:00
th-kim0823
7f5739d8fb 🏗️ refactor(fb-31): apply round 1 review nits
- ingest_file_with_config: lowercase normalize ext (caller-side) +
  early error on unsupported extension (`.docx` etc. now Err with
  helpful message instead of silent skipped_by_extension counter).
  New test ingest_file_errors_on_unsupported_extension.
- ingest_stdin_with_config: doc comment explaining intentional
  double-call of ensure helpers (idempotent + ~ms negligible).
- external::inject_frontmatter: simplify precheck via single
  trim_start binding + add CR-only line ending edge case.
- external::inject_frontmatter: doc note on yaml_quote escape
  contract (agent-supplied titles with special chars are safe).

Round 1 review summary: #111 (comment)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:46:55 +09:00
th-kim0823
dc24cb34b1 🚑 fix(fb-31): apply final review nits
- kebab-app: add #[doc(hidden)] to ingest_stdin_with_config (CLAUDE.md
  convention — all *_with_config functions should have this attribute;
  fb-31's first impl missed it on the second facade fn).
- SKILL.md: "Since v0.4.0" → "Since v0.3.1" (MCP shipped in fb-30
  release v0.3.1; the wrong version claim was introduced in fb-30 doc
  sync and carried forward into fb-31).
- tools_call_ingest_file: add idempotency test (second call with same
  content → unchanged=1, new=0). Spec called for two tests; first impl
  shipped only the happy path.

Version bump 0.3.1 → 0.3.2 deferred to separate `chore/bump-v0.3.2` PR
mirroring fb-27 + fb-30 precedent (commits 73f5d73 / 5495d96).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:38:19 +09:00
th-kim0823
ccee30037d 🧪 test(kebab-cli): update cli_mcp_smoke tools/list assertion 4 → 6 (fb-31)
fb-31 added ingest_file + ingest_stdin MCP tools (Task 9) but the
spawn-based smoke test in cli_mcp_smoke.rs still asserted the fb-30
count of 4. Bump to 6 to match the live tools/list response.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:26:51 +09:00
th-kim0823
e041173e8e 📝 docs(tasks): HOTFIXES entry + p9-fb-31 status → completed
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:18:50 +09:00
th-kim0823
345a4f363a 📝 docs: sync README / HANDOFF / CLAUDE / skill / design for fb-31
- README 명령 표 에 `kebab ingest-file` + `kebab ingest-stdin` 두 row + MCP tool list 4 → 6.
- HANDOFF post-도그푸딩 항목 한 줄.
- CLAUDE.md `_external/` 디렉토리 + naming convention 한 줄.
- integrations skill — Recipe D (agent fetched web doc) + MCP tool list 갱신.
- design §6.7 `_external/` subdirectory 절 신설.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:16:45 +09:00
th-kim0823
71c2bbdc97 🧪 test(kebab-mcp): ingest_file + ingest_stdin integration (fb-31)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:14:07 +09:00
th-kim0823
ecd77290cd feat(kebab-mcp): ingest_file + ingest_stdin tools (fb-31)
5th + 6th MCP tools — first mutation surface (fb-30 v1 was read-only).
Both wrap the new kebab-app facade fns + use spawn_blocking via the
existing spawn_tool helper. tools/list now returns 6 tools.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:12:18 +09:00
th-kim0823
fbc01eda50 🧪 test(kebab-cli): cli_ingest_file + cli_ingest_stdin integration (fb-31)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:10:09 +09:00
th-kim0823
0386adcb5e feat(kebab-cli): kebab ingest-stdin subcommand (fb-31)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:07:35 +09:00
th-kim0823
9cc7deca11 feat(kebab-cli): kebab ingest-file subcommand (fb-31)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:06:25 +09:00
th-kim0823
a42f907640 🧪 test(kebab-app): ingest_stdin_with_config integration (fb-31)
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:04:52 +09:00
th-kim0823
67050016cc feat(kebab-app): ingest_stdin_with_config facade (fb-31)
Wraps body with YAML frontmatter (title + source_uri) via
crate::external::inject_frontmatter, writes to
_external/<hash12>.md, delegates to ingest_file_with_config. Markdown
only in v1.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:04:01 +09:00
th-kim0823
73ee64c73f 🧪 test(kebab-app): ingest_file_with_config integration (fb-31)
Three scenarios — copies external md + reports new=1, idempotent on
second call (unchanged=1), errors on missing path.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:02:52 +09:00
th-kim0823
9b53dcb94f feat(kebab-app): ingest_file_with_config facade (fb-31)
Single-file ingest entry. Copies bytes to _external/<hash12>.<ext>
via crate::external::copy_to_external, runs the per-medium pipeline on
that single asset (reuses ingest_with_config_opts via a SourceScope
{ root: _external/, include: [<filename>], exclude:
config.workspace.exclude }).

`.kebabignore` matches log a stderr warn line and proceed (explicit
ingest is bypass intent). Internal helper `check_kebabignore_match`
uses the `ignore` crate's GitignoreBuilder.

Returns the standard IngestReport (incremental ingest from fb-23
handles re-ingest as `unchanged`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 18:01:18 +09:00
th-kim0823
41061a38ac 🏗️ feat(kebab-app): external module — _external dir + frontmatter inject (fb-31)
Pure-fn helpers for the `_external/` workspace subdirectory:
- `ensure_external_dir(workspace_root)` — mkdir if absent
- `ensure_kebabignore_entry(workspace_root)` — append `_external/` line
  to .kebabignore if missing (idempotent)
- `copy_to_external(ext_dir, bytes, ext)` — write to
  `<ext_dir>/<blake3-12>.<ext>`, idempotent on same content
- `inject_frontmatter(body, title, source_uri?)` — prepend YAML block
  with strict double-quote escaping; errors if body already starts
  with `---`
- `yaml_quote(s)` — defensive escaping for agent-supplied strings

14 unit tests cover happy + idempotency + edge (CRLF frontmatter
detection, YAML escape).

ingest_file / ingest_stdin facades (Tasks 2-5) compose these.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 17:58:31 +09:00