[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-75458":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":8,"htmlUrl":8,"language":9,"languages":8,"totalLinesOfCode":8,"stars":10,"forks":11,"watchers":12,"openIssues":13,"contributorsCount":14,"subscribersCount":14,"size":14,"stars1d":14,"stars7d":13,"stars30d":15,"stars90d":14,"forks30d":14,"starsTrendScore":14,"compositeScore":16,"rankGlobal":8,"rankLanguage":8,"license":17,"archived":18,"fork":18,"defaultBranch":19,"hasWiki":20,"hasPages":18,"topics":21,"createdAt":8,"pushedAt":8,"updatedAt":22,"readmeContent":23,"aiSummary":24,"trendingCount":14,"starSnapshotCount":14,"syncStatus":25,"lastSyncTime":26,"discoverSource":27},75458,"kwwk","EYHN\u002Fkwwk","EYHN",null,"Swift",118,9,114,1,0,4,3,"MIT License",false,"main",true,[],"2026-06-12 02:03:34","# kwwk\n\nA Swift-native coding agent with two faces:\n\n- **`kwwk`** — an interactive coding CLI (TUI) that drives your existing\n  Anthropic, ChatGPT (Codex), Gemini, or GitHub Copilot subscription.\n- **`KWWKAgent` \u002F `KWWKAI`** — the agent runtime underneath, exposed as\n  SwiftPM libraries so you can embed it in your own app, build custom\n  tools, or swap the LLM provider.\n\n## Requirements\n\n- macOS 14+\n- Swift 6.1 toolchain (Xcode 16.3+ or the matching `swift` toolchain)\n\n---\n\n## 1. The coding CLI\n\n### Install\n\nFrom Homebrew (recommended):\n\n```sh\nbrew install EYHN\u002Ftap\u002Fkwwk\n```\n\nOr build from source:\n\n```sh\nswift build -c release\ncp .build\u002Frelease\u002Fkwwk \u002Fusr\u002Flocal\u002Fbin\u002F\n```\n\n### Run\n\n```\nkwwk              launch the interactive coding TUI\nkwwk login        log in to an OAuth provider\nkwwk --help       show this message\n```\n\nCredentials come from the OAuth store at `~\u002F.kwwk\u002Foauth.json` — run\n`kwwk login` once to register a provider (OAuth subscription like\nChatGPT Codex, Gemini, Copilot, or Claude Code; or an API key for\nAnthropic, OpenAI, Google, or any OpenAI-compatible endpoint).\n\nInside the TUI, `\u002Fhelp` lists slash commands (`\u002Fmodel`, `\u002Fthinking`,\n`\u002Fclear`, …). The agent ships with Bash, Read, Write, Edit, Grep, Find,\nLS, tmux, and background-task tools out of the box.\n\n---\n\n## 2. The agent SDK\n\nAdd `kwwk` as a SwiftPM dependency:\n\n```swift\n.package(url: \"https:\u002F\u002Fgithub.com\u002FEYHN\u002Fkwwk\", branch: \"main\"),\n```\n\nThen depend on the libraries you need:\n\n```swift\n.product(name: \"KWWKAgent\", package: \"kwwk\"),\n.product(name: \"KWWKAI\",    package: \"kwwk\"),\n```\n\n- **`KWWKAI`** — model clients, provider registry, streaming, OAuth,\n  message \u002F tool types.\n- **`KWWKAgent`** — the turn\u002Ftool loop, built-in coding tools, hooks.\n\n### Quick start — one-shot run\n\n`Agent.runOnce` mirrors `query()` in the Python Agent SDK: a fresh agent\nruns a single prompt and yields every event as an async stream.\n\n```swift\nimport KWWKAI\nimport KWWKAgent\n\n\u002F\u002F 1. Register a provider using an API key.\nawait registerBuiltins(anthropic: ProcessInfo.processInfo.environment[\"ANTHROPIC_API_KEY\"])\n\n\u002F\u002F 2. Build a coding agent scoped to a working directory.\nlet agent = await makeCodingAgent(CodingAgentConfig(\n    model: Models.claudeSonnet45,\n    cwd: FileManager.default.currentDirectoryPath,\n    tools: .all\n))\n\n\u002F\u002F 3. Drive it.\ntry await agent.prompt(\"Summarize the Swift files under Sources\u002FKWWKAgent.\")\n\n\u002F\u002F 4. Read the transcript.\nfor message in agent.state.messages {\n    print(message)\n}\n```\n\n### Subagents\n\n`CodingAgentConfig.subagents` defaults to an empty array. When it is\nempty, `makeCodingAgent` does not register the `agent` tool. Add\nsubagent definitions explicitly when you want model-driven delegation:\n\n```swift\nlet reviewer = SubagentDefinition(\n    name: \"reviewer\",\n    description: \"Use for code quality, security, maintainability, and test coverage review.\",\n    prompt: \"\"\"\n    You are a senior code reviewer. Review code carefully, do not edit files,\n    and report findings with file paths, severity, and concrete evidence.\n    \"\"\",\n    tools: .readOnly,\n    model: .inherit\n)\n\nlet bg = BackgroundTaskManager()\nlet agent = await makeCodingAgent(CodingAgentConfig(\n    model: Models.claudeSonnet45,\n    cwd: FileManager.default.currentDirectoryPath,\n    tools: .all,\n    backgroundManager: bg,\n    subagents: [reviewer]\n))\n\ntry await agent.prompt(\"Use the reviewer subagent to review Sources\u002FKWWKAgent.\")\n```\n\nFor the same built-ins that the CLI uses, SDK users can opt in without\ncopying prompts:\n\n```swift\nlet agent = await makeCodingAgent(CodingAgentConfig(\n    model: Models.claudeSonnet45,\n    cwd: FileManager.default.currentDirectoryPath,\n    tools: .all,\n    backgroundManager: BackgroundTaskManager()\n).withBuiltinSubagents([.general, .explore, .plan]))\n```\n\nSDK users can also run a subagent directly:\n\n```swift\nlet runner = SubagentRunner(\n    cwd: FileManager.default.currentDirectoryPath,\n    subagents: [.plan()],\n    parentModel: Models.claudeSonnet45\n)\nlet result = try await runner.run(\n    type: \"Plan\",\n    prompt: \"Plan how to simplify Sources\u002FKWWKAgent\u002FSubagentTool.swift.\"\n)\n```\n\nSubagents are fresh-context agents: they do not inherit the parent\ntranscript. The parent model must put the relevant files, errors, goals,\nand constraints into the `agent` tool's `prompt`. Subagents inherit the\nparent model, thinking level, retry delay, and API key resolver, but they\ndo not inherit parent hooks such as `betweenTurns`, `transformContext`,\n`convertToLlm`, `userPromptSubmit`, or tool-call hooks.\n\nEach subagent run gets its own child session id. Tools inside that\nsubagent, including background-capable tools such as Bash, are scoped to\nthe child session. While the child agent is running, background task\nnotifications are attached to that child session. When the subagent\nfinishes or is cancelled, the generic background-task session is closed:\nstill-running tasks in that child session are killed and queued\nnotifications for that child session are discarded. If the parent starts\nthe subagent itself with `run_in_background`, that top-level subagent job\nremains parent-visible so `wait_task` and completion notifications still\nwork.\n\nIn the interactive TUI, foreground subagent tool calls update their\nin-flight display with the child agent's token usage as it runs. When a\nprovider does not stream exact usage until the end of the turn, the live\ncounter falls back to an approximate output-token estimate and is\nreplaced by provider-reported usage once available.\n\nSubagent tools also emit structured runtime events through\n`AgentEvent.runtimeEvent(.subagent(...))`: started, tool update,\nbackground started, completed, and failed. The terminal\n`AgentRunSummary.subagents` array records each foreground child run's\nusage, cost, turns, duration, status, model, and child session id.\nBackground subagents are recorded when the parent-visible background task\nis started; their completion is delivered through the existing\nbackground-task notification flow.\n\nThe interactive `kwwk` CLI enables a small built-in set by default:\n`general`, `Explore`, and `Plan`. `general` has wildcard tools by\ndefault and is used when the model omits `subagent_type`. `Explore` and\n`Plan` are read-only specialists. Use `--no-subagents` to disable them\nor `--subagents general,Explore` to enable only a subset. The SDK does\nnot enable those automatically.\n\nWhen an SDK application is done with an agent session, call\n`await agent.closeSession()` to release provider-owned resources keyed by\nthat session id. For OpenAI Responses WebSocket transport, this closes the\nstored WebSocket connection for the session.\n\n### Streaming events\n\nSubscribe before prompting to observe tokens, tool calls, and the final\nsummary as they happen:\n\n```swift\nlet unsubscribe = agent.subscribe { event, _ in\n    switch event {\n    case .messageUpdate(let assistant, _):\n        \u002F\u002F Live-render streaming assistant tokens.\n        print(assistant.textPreview, terminator: \"\")\n    case .toolExecutionStart(_, let name, let args):\n        print(\"→ \\(name) \\(args)\")\n    case .agentEnd(_, let summary):\n        print(\"\\n[\\(summary.turns) turns · $\\(summary.cost.total)]\")\n    default: break\n    }\n}\ndefer { unsubscribe() }\n\ntry await agent.prompt(\"Find all TODOs in this repo.\")\n```\n\nOr consume `runOnce` as an `AsyncThrowingStream`:\n\n```swift\nfor try await event in Agent.runOnce(\n    prompt: \"what's in README.md?\",\n    options: AgentOptions(initialState: AgentInitialState(\n        model: Models.claudeHaiku45,\n        tools: [createReadTool(cwd: \".\")]\n    ))\n) {\n    if case .messageEnd(let message) = event { print(message) }\n}\n```\n\n### Custom tools\n\nA tool is a name, a JSON-Schema parameter spec, and an async `execute`\nclosure. The agent handles validation, cancellation, and wiring the\nresult back into the transcript.\n\n```swift\nimport KWWKAI\nimport KWWKAgent\n\nlet weather = AgentTool(\n    name: \"get_weather\",\n    label: \"weather\",\n    description: \"Look up the current temperature for a city.\",\n    parameters: [\n        \"type\": \"object\",\n        \"properties\": [\n            \"city\": [\"type\": \"string\", \"description\": \"City name\"]\n        ],\n        \"required\": [\"city\"]\n    ],\n    execute: { _, args, _, _ in\n        guard case .object(let obj) = args,\n              case .string(let city) = obj[\"city\"] ?? .null else {\n            throw CodingToolError.invalidArgument(\"city required\")\n        }\n        let temp = try await fetchTemp(city)\n        return AgentToolResult(content: [.text(.init(text: \"\\(temp)°C in \\(city)\"))])\n    }\n)\n\nlet agent = Agent(initialState: AgentInitialState(\n    model: Models.claudeSonnet45,\n    tools: [weather]\n))\ntry await agent.prompt(\"Is it warmer in Tokyo or Oslo right now?\")\n```\n\n### Hooks — audit, redact, short-circuit\n\nEvery `AgentOptions` accepts hooks that fire at well-defined points. Use\nthem to enforce policy without forking the loop:\n\n```swift\nlet options = AgentOptions(\n    initialState: AgentInitialState(model: Models.claudeSonnet45, tools: [...]),\n    \u002F\u002F Block or rewrite a tool call before it runs.\n    beforeToolCall: { ctx, _ in\n        if ctx.toolCall.name == \"bash\",\n           case .object(let o) = ctx.args,\n           case .string(let cmd) = o[\"command\"] ?? .null,\n           cmd.contains(\"rm -rf\") {\n            return BeforeToolCallResult(block: true, reason: \"destructive command blocked\")\n        }\n        return nil\n    },\n    \u002F\u002F Intercept a user prompt before it enters the transcript.\n    userPromptSubmit: { ctx, _ in\n        \u002F\u002F e.g. redact secrets, inject policy preamble.\n        return nil\n    }\n)\nlet agent = Agent(options: options)\n```\n\nOther hook points: `afterToolCall`, `convertToLlm`, `transformContext`\n(for context pruning \u002F summarization).\n\n### Steering a running agent\n\nQueue a message that will be injected at the next turn boundary —\nwithout aborting the current turn:\n\n```swift\nTask {\n    try await agent.prompt(\"refactor this module end-to-end\")\n}\n\n\u002F\u002F later, from any thread:\nagent.steer(UserMessage(text: \"also add tests as you go\"))\n```\n\n### Providers\n\n`registerBuiltins` covers Anthropic, OpenAI (Completions + Responses),\nand Google Gemini. `Models` exposes a small curated catalog\n(`claudeSonnet45`, `gpt5`, `gemini25Pro`, …) or you can construct\n`Model` values by hand. For OpenAI-compatible endpoints (xAI, Groq,\nOpenRouter) there are `Models.xaiGrok(id:)`, `Models.groq(id:)`,\n`Models.openRouter(id:)` helpers.\n\nTo use a subscription (OAuth) token instead of a raw API key, drive the\nflow via `KWWKAI.OAuth` \u002F `OAuthLogin` — the same code path the CLI's\n`kwwk login` command uses.\n\n### Updating the model catalog\n\n`\u002Fmodel` reads the bundled catalog at\n`Sources\u002FKWWKAI\u002FResources\u002Fmodels.json`, generated from pi-mono's\n`packages\u002Fai\u002Fsrc\u002Fmodels.generated.ts`.\n\n```sh\nswift run kwwk-generate-models \u002Fpath\u002Fto\u002Fpi-mono\u002Fpackages\u002Fai\u002Fsrc\u002Fmodels.generated.ts\nswift test\n```\n\nThe generator writes `Sources\u002FKWWKAI\u002FResources\u002Fmodels.json` by default.\nThe catalog tests assert unsupported Google Gemini CLI and Google\nAntigravity provider groups stay absent.\n\n---\n\n## Layout\n\n- `Sources\u002FKWWKAI` — model clients, OAuth, provider adapters\n- `Sources\u002FKWWKAgent` — tool-using agent loop and built-in tools\n- `Sources\u002FKWWKCli` — interactive TUI, slash commands, rendering\n- `Sources\u002Fkwwk` — the executable entry point\n- `Tests\u002F` — XCTest suites for each module\n\n```sh\nswift test\n```\n\n## A note on OAuth client IDs\n\n`Sources\u002FKWWKAI\u002FOAuthProviders.swift` reuses the OAuth client IDs (and,\nwhere applicable, public app metadata) shipped by the upstream\nfirst-party CLIs — Anthropic's Claude Code, OpenAI's Codex CLI, and\nGitHub Copilot's VS Code extension. Those credentials are not secrets in\nany meaningful sense — they are embedded in those open-source CLIs and\nare required for the \"log in with your existing subscription\" flow to\nwork. They remain the property of their respective vendors, who may\nrotate or revoke them at any time. `kwwk` is not affiliated with or\nendorsed by any of these vendors.\n\n## License\n\nMIT — see [LICENSE](LICENSE).\n","kwwk 是一个基于 Swift 的编码助手，具备交互式命令行界面（TUI）和可嵌入的代理运行时。该项目的核心功能包括通过现有的Anthropic、ChatGPT (Codex)、Gemini或GitHub Copilot订阅提供编程支持，同时以SwiftPM库的形式提供了`KWWKAgent`和`KWWKAI`，便于开发者将其集成到自己的应用中，创建自定义工具或更换大语言模型提供商。适用于需要在macOS 14及以上版本环境中快速开发或改进代码的工作场景，特别是对于希望利用AI增强编码效率的个人开发者或团队来说，kwwk 提供了一个强大而灵活的选择。",2,"2026-06-11 03:52:51","CREATED_QUERY"]