npm-cli-github-issue-tower-defence-management
欢迎访问npm-cli-github-issue-tower- defense -management:tada:
使用🛠️
下面是你如何使用github-issue-tower-defence-management:
Usage: github-issue-tower-defence-management [command] [options]
CLI tool for GitHub Issue Tower Defence Management
Commands:
schedule [options] Handle scheduled events (trigger: issue or schedule) (default)
startDaemon [options] Start daemon to prepare GitHub issues
notifyFinishedIssuePreparation [options] Notify that issue preparation is finished
selectLlmLaunchFlags [options] Print the --effort and --autocompact launch flags for a worker session, derived from the defaultLlmEffortLevel and defaultLlmAutocompactMode fields of the config file at --configFilePath. Writes "--effort <value>" using defaultLlmEffortLevel, falling back to "xhigh" when defaultLlmEffortLevel is unset or empty. Writes "--autocompact <value>" after it on the same stdout line only when defaultLlmAutocompactMode is set to a non-empty value. The launcher decides no value itself and only forwards these flags to claude-agent, the same split as the selectLiveSessionOauthToken command.
checkIssueReviewReadiness [options] Check whether an issue is review-ready (read-only; does not change Status or post any comment)
serveWeb [options] Start the local TDPM web server (console tabs, dashboard, and in-tmux session list)
serveConsole [options] Deprecated alias for serveWeb. Use serveWeb instead.
selectOauthToken [options] Print one rate-limit-aware Claude Code OAuth token to stdout (pipeable; read-only)
selectLiveSessionOauthToken [options] Print one Claude Code OAuth token chosen for a new live interactive session (soonest 7d reset among tokens under their concurrent session limit; read-only)
countInTmuxByHumanSessionsPerToken [options] Print, per OAuth token, the count of live "In Tmux by human" interactive sessions using that token (read-only)
killTmuxSession [options] Cleanly kill a tmux session by running tmux kill-session and stopping its cl-*.scope systemd --user unit
attachOrCreate [options] Attach to an existing registered tmux session for the issue URL, or create a new one
ownerCallFileAppend [options] Append one owner call to the per-session owner call file (writes nothing to stdout)
ownerCallFileDelete [options] Delete the per-session owner call file (writes nothing to stdout; an already absent file is not an error)
archive-unresumable-session [options] Move aside the conversation record of a session that ended because its prompt exceeded the model context limit, so the next launch starts a fresh conversation
select-resumable-session [options] Print the claude session id a dispatched worker resumes, or nothing when it starts a new claude session, copying the transcript file from another worktree when needed
check-issue-silent-dispatch-allowed [options] Check whether an issue's body allows a dispatch cycle to end with no agent comment (read-only)
workerRequestTextRead [options] Print the request text a worker session receives for an issue, "Take ownership of <issue url>" (read-only)
help [command] display help for command
Options for schedule:
-t, --trigger <type> Trigger type: issue or schedule
-c, --config <path> Path to config YAML file
-v, --verbose Verbose output
-i, --issue <url> GitHub Issue URL
--inTmuxProjectOrder <names> Comma-separated project names, in display order, for the in-tmux-by-human session list. When omitted, falls back to the inTmuxProjectOrder value in the config file.
Options for startDaemon:
--configFilePath <path> Path to config file for tower defence management (required)
--projectUrl <url> GitHub project URL
--manager <login> GitHub login of the manager; only Awaiting Workspace issues assigned to this login are picked up (required)
--defaultAgentName <name> Default agent name
--defaultLlmModelName <name> Default LLM model name
--defaultLlmAgentName <name> Default LLM agent name
--maximumPreparingIssuesCount <count> Maximum number of issues in preparation status (default: 6 per available Claude OAuth token, otherwise 6)
--utilizationPercentageThreshold <percent> 5-hour utilization hard threshold; tokens at or above this percentage are excluded from rotation. Per-token concurrency also tapers from 6 slots down to 1 as either the 5h or 7d utilization rises from 80% toward 100%, taking the more restrictive of the two (default: 90)
--allowedIssueAuthors <authors> Comma-separated list of allowed issue authors
--preparationProcessCheckCommand <template> Shell command template with {URL} placeholder to check if a preparation process is alive
--fleetConfigFilePath <path> Path to the fleet-wide YAML config file holding the preparationWorker mapping (normalConcurrentLimit); falls back to the TDPM_FLEET_CONFIG environment variable, and to the built-in values when neither is set
Options for notifyFinishedIssuePreparation:
--configFilePath <path> Path to config file for tower defence management (required)
--issueUrl <url> GitHub issue URL (required)
--projectUrl <url> GitHub project URL
--thresholdForAutoReject <count> Threshold for auto-escalation after consecutive rejections (default: 3)
--thresholdForDispatchLoop <count> Threshold for auto-escalation after one agent is dispatched this many times since the last human comment, whether it names itself or two agents name each other in turn (default: 6)
--workflowBlockerResolvedWebhookUrl <url> Webhook URL to notify when a workflow blocker issue status changes
--missingAgentName <name> Agent definition name that was not found; triggers task issue creation (assigned to the manager from config) and blocks the item until that issue is closed
--sessionErrorLine <line> Exact error line from the session log to include in the task issue body
--deferPreparation Defer the item via the Reactivation Trigger fields (sets nextActionDate to tomorrow) and return it to Awaiting Workspace without creating any issue, recording the value of --sessionErrorLine as the stop reason to the console log; use for transient upstream failures that should retry the next day
--moveToFailedPreparation Move the item to Failed Preparation status after reaching the consecutive failure threshold; takes precedence over --deferPreparation when both are supplied; records --sessionErrorLine in the comment
--fleetConfigFilePath <path> Path to the fleet-wide YAML config file holding the workflowIssueReporter mapping; falls back to the TDPM_FLEET_CONFIG environment variable. When set and the file contains a workflowIssueReporter section, a silent-redispatch escalation automatically creates (or comments on) an issue in the configured repository instead of only logging locally.
--dispatchStartedAt <timestamp> ISO-8601 UTC timestamp (for example 2026-01-31T09:00:00Z) at which the item entered Preparation for the session that just ended. An agent report posted before this time is not counted as that session's report, so a session that posts nothing is recorded as NO_REPORT_FROM_AGENT_BOT and counts toward the consecutive-no-report threshold (thresholdForAutoReject) that moves the item to Failed Preparation. When omitted, the latest agent report on the issue is used whatever its age.
--sessionLogFilePath <path> Path to the log of the worker session that just ended. The command reads it once, before the notification, and sets --rateLimitRejected for a usage-limit ending, or for a rejected rate-limit event when the session ended neither with terminal_reason blocking_limit nor on an HTTP 429 session-limit error, --promptTooLongOnResume for a resumed session that ended with terminal_reason blocking_limit before any tool call, and --moveToFailedPreparation with its own --sessionErrorLine on the third consecutive ending with the same non-completed terminal_reason. The consecutive count is kept per issue under the TDPM cache directory (worker-session-failure-streaks) and is reset by a completed ending or a log without a terminal_reason. The result is combined with the explicit flags; usage-limit and rate-limit classification is skipped when a non-empty --missingAgentName is given, which also keeps the count from moving the item to Failed Preparation.
--sessionWasResumed The worker session that just ended resumed a previous conversation; used with --sessionLogFilePath to decide --promptTooLongOnResume
The notification is attempted up to 3 times, waiting 30 seconds and then 60 seconds after a failed attempt; the session log is classified only once. A GitHub API rate limit ends the command without an error and without a retry, and the third failure ends the command with that error.
Options for selectLlmLaunchFlags:
--configFilePath <path> Path to config file for tower defence management (required)
Options for checkIssueReviewReadiness:
--configFilePath <path> Path to config file for tower defence management (required)
--issueUrl <url> GitHub issue URL (required)
--projectUrl <url> GitHub project URL
Options for serveWeb (and its deprecated alias serveConsole):
--configFilePath <path> Path to config file for tower defence management (required)
--port <number> Port for the web HTTP server (default: 9980)
--consoleDataOutputDir <path> Directory where console data files are written and served from
--inTmuxDataDir <path> Directory containing the flat in-tmux-by-human static JSON files served at /in-tmux-by-human/*.json
--dashboardDir <path> Directory containing the static dashboard HTML fragment tdpm.txt served at /tdpm.txt when compose mode is not active (default: the jsonpub directory)
--dashboardDataDir <path> Directory containing the dashboard data files (projects/<projectName>.json, machine-status.json, token-status.json); when set and at least one listed project has its own data file present the server composes the /tdpm.txt fragment live from whatever data is available, showing -- for any project without a file, otherwise it falls back to serving the static tdpm.txt from --dashboardDir (unset when not configured)
--dashboardProjectNames <names> Comma-separated project names, in display order, for the dashboard project grid; the display label of each project is its first 2 characters, which must be unique across the listed names
--fleetConfigFilePath <path> Path to the fleet-wide YAML config file; falls back to the TDPM_FLEET_CONFIG environment variable. When the file contains a top-level `workflowImprovementIssueUrl` string, the console tab bar displays a link that opens that URL in a new browser tab. When the file contains a `workflowIssueReporter` section with `owner` and `repo` fields, the console tab bar displays a + link for creating fleet-level tasks; the link opens `https://github.com/{owner}/{repo}/issues/new?assignees={owner}` in a new browser tab, pre-assigning the owner, and, when `projectUrl` starts with a GitHub org (`https://github.com/orgs/{org}/projects/{n}`) or user (`https://github.com/users/{user}/projects/{n}`) project URL (optionally followed by a `/views/N` path suffix or query parameters such as `?fullscreen=true`), also appends `?projects={owner}/{n}` so the new issue is automatically added to that project.
Options for selectOauthToken:
--tokenListJsonPath <path> Path to the JSON array of { name, token, selectionWeight? } records; selectionWeight is an optional positive number (default 1) that biases how often a token is chosen among eligible candidates (falls back to the CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH environment variable)
--cacheDir <path> Directory holding per-token rate-limit cache files (falls back to the TDPM_RATELIMIT_CACHE_DIR environment variable, then to ${XDG_CACHE_HOME:-~/.cache}/tdpm/ratelimit)
Options for selectLiveSessionOauthToken:
--tokenListJsonPath <path> Path to the JSON array of { name, token, selectionWeight? } records; selectionWeight is an optional positive number (default 1) that scales this token concurrent live session limit; a smaller weight allows fewer simultaneous sessions and never bypasses eligibility filtering or starves a sole eligible token. Falls back to the CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH environment variable.
--cacheDir <path> Directory holding per-token rate-limit cache files (falls back to the TDPM_RATELIMIT_CACHE_DIR environment variable, then to ${XDG_CACHE_HOME:-~/.cache}/tdpm/ratelimit)
--fleetConfigFilePath <path> Path to the fleet-wide YAML config file holding the liveSessionOauthTokenSelection mapping (maxConcurrentSessionCount, fullSpeedFiveHourFreeRatio, minFiveHourFreeRatio, minSevenDayFreeRatio, fiveHourShareConsumedPerSessionHour); falls back to the TDPM_FLEET_CONFIG environment variable, and to the built-in values when neither is set
Options for countInTmuxByHumanSessionsPerToken:
--configFilePath <path> Path to config file for tower defence management (required)
--projectUrl <url> GitHub project URL
--tokenListJsonPath <path> Path to the JSON array of { name, token } records (falls back to the claudeCodeOauthTokenListJsonPath config value, then to the CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH environment variable)
Options for killTmuxSession:
--session <name> Name of the tmux session to kill
--self Terminate the current session by stopping its own cl-*.scope systemd user unit, derived from /proc/self/cgroup
Options for attachOrCreate:
--issueUrl <url> GitHub issue URL to attach to or create a session for (required)
Options for ownerCallFileAppend:
--session <name> tmux session name that raised the call (required)
--calledAt <timestamp> Time the call was raised, as UTC ISO-8601 with second precision and a trailing Z (required)
--body-file <path> Path to the file holding the call body; the body is read from a file because it is multi-line and can be long (required)
--inTmuxDataDir <path> Directory the owner call files are written under, the same directory serveWeb serves them from
Options for ownerCallFileDelete:
--session <name> tmux session name that raised the call (required)
--inTmuxDataDir <path> Directory the owner call files are written under, the same directory serveWeb serves them from
Options for archive-unresumable-session:
--log-file <path> Path to the finished run stream-json log (required)
--session-dir <path> Directory holding conversation records named <session-id>.jsonl (required)
--archive-dir <path> Destination directory the conversation record is moved into; created when it does not exist (required)
Options for select-resumable-session:
--session-name <name> Name the worker claude session is started with (claude --name); a transcript file is a candidate only when its first line contains "customTitle":"<name>" (required)
--session-dir <path> Session directory holding the transcript files (<session id>.jsonl) of the current working directory of the worker; claude --resume <session id> finds transcript files only in this directory (required)
--archive-root <path> Directory archive-unresumable-session moves transcript files into; a transcript file is archived when <archive-root>/<session id>.jsonl or <archive-root>/<directory>/<session id>.jsonl exists (required)
--other-session-dir <path> Session directory of another worktree of the same repository, searched when --session-dir yields no resumable transcript file; repeatable, and of transcript files with the same modification time the one in the directory given first wins
Options for check-issue-silent-dispatch-allowed:
--issue-url <url> GitHub issue URL (required)
Options for workerRequestTextRead:
--issueUrl <url> GitHub issue URL the worker session takes ownership of (required)
这个 serveWeb 子命令启动一个本地HTTP服务器,该服务器服务于TDPM web界面——控制台选项卡、仪表板和in-tmux-by-human会话列表。 serveConsole 作为映射到相同处理程序的弃用别名仍然可用,因此现有调用者在推出期间继续工作;新用法应优先 serveWeb. 一个运行的实例服务于每个项目:用户打开每个项目的URL路径 /projects/{pjcode} (或 /projects/{pjcode}/{workflow-blocker|prs|triage|unread|failed-preparation|todo-by-human}),绑定的UI读取 pjcode 从它自己的URL路径加载项目的列表数据。服务器服务于绑定的单页面应用程序 index.html 在 /, /index.html,每一个 /projects/{pjcode} 和 /projects/{pjcode}/{tab} 应用路线。每个响应发送 Cache-Control: no-store. 任何包含以点开头的段的请求路径(例如 /.git 或 /.env)被HTTP 404拒绝。UI引导资产(HTML和JS)不需要身份验证;服务 *.json 文件和 /api/* 路径需要访问令牌作为 k 查询参数(?k=<token>)或 X-PV-Token 请求头。访问令牌从 consoleAccessToken 配置值,并且永远不会出现在命令行上。当构建的UI bundle目录(ui-dist)不在时,服务器仍会启动并为其提供最小占位符索引 /, /index.html,以及每个项目的应用路由。
指示板片段在以下位置提供未经身份验证的服务 GET /tdpm.txt 作为 text/html. 撰写模式是可选的:当 --dashboardDataDir 中指定了至少一个项目 --dashboardProjectNames 有它自己的 projects/<projectName>.json 可读的,服务器在请求时从任何的内容实时合成片段 machine-status.json, token-status.json,以及每个项目 projects/<projectName>.json 文件实际上是存在的-一个没有可读数据文件的项目呈现 -- 在其排独立于其他的,和一个失踪 machine-status.json 或 token-status.json 不再阻挡构图。每一个礼物 projects/<projectName>.json 以项目的完整名称作为键(计划运行写入的键相同;该项目的可操作状态计数), machine-status.json 包含内存%、cpu%、磁盘%、loadavg、循环分钟等 token-status.json 保存每个令牌的速率限制利用率、重置倒计时、状态颜色和准备/嗡嗡计数。它呈现固定宽度的片段- host-metrics行({memDot}M{mem}% {cpuDot}C{cpu}% {diskDot}D{disk}% cy{cycle} {loadDot}LA {load1} {load5} {load15} 对于无磁盘情况;每个点🔴处于或高于危险阈值(mem/cpu/磁盘≥90%,load1m≥10)或🟡处于或高于警告阈值(mem/cpu/磁盘≥80%,load1m≥5),当指标正常时不存在;每个磁盘的情况仍然发出多行),项目网格(汇总包含数据的项目中的每列的行,然后是标题,然后是每个名称中的一行 --dashboardProjectNames 顺序,用项目的两个字符的显示代码(来自项目名称,前缀是严重性点)标记,一个空白分隔线,然后按最快7天重置排序每个标记一行-包裹起来 <tt>…</tt><br> 空格编码为 . 否则-当 --dashboardDataDir 未设置,或者设置了,但列出的项目中没有任何数据文件存在-服务器退回到静态服务 tdpm.txt byte-for-byte从 --dashboardDir,与撰写模式存在之前完全相同。路由只有在两个源都不可用时才返回HTTP 404(撰写模式无效且没有静态模式) tdpm.txt 礼物)。只有 GET 在此路径上被接受。
在令牌门后面,服务器公开了三组路由:
- 数据传递(GET):
GET /projects/{pjcode}/{workflow-blocker|prs|triage|unread|failed-preparation|todo-by-human}/list.json以及匹配detail/<key>.json文件从{consoleDataOutputDir}/{pjcode}/{tab}/,GET /projects/{pjcode}/in-tmux-by-human/*是从{consoleDataOutputDir}/{pjcode}/in-tmux-by-human/. 每个服务列表都有.done应用了交叉选项卡排除。调度周期写入完整的选项卡文件;notifyFinishedIssuePreparation此外,在每次状态转换后立即修补受影响的TAB文件。 - 由人编写的静态文件(GET):
GET /in-tmux-by-human/<file>.json提供平面静态JSON文件(例如index.v4.json和{project}.v4.json(加上v3文件以向后兼容)字节对字节的--inTmuxDataDir. 只提供直接在该目录下的平面文件名;任何其他嵌套路径和任何路径遍历尝试都会被HTTP 404拒绝。这条路线不同于每个项目/projects/{pjcode}/in-tmux-by-human/*上面的数据传递路径。这些文件仍然由相同的访问令牌进行门控,因此请求可以携带?k=<token>. 令牌密钥文件的生成由外部监视器执行,而不是由该服务器执行。 - 每会话所有者调用文件(GET和DELETE):
GET /in-tmux-by-human/call-to-user/{pjcode or NA}/{session}.yaml为一个会话的所有者调用文件提供字节对字节的服务--inTmuxDataDir,因为text/yaml,DELETE在同一路径上删除它并回答HTTP 204文件是否仍然存在,因此删除两次的客户端不会出错。只接受这种单目录深的形状;DELETE在前缀下的所有其他路径上都被HTTP 404拒绝,包括平面JSON文件。这两种方法都位于与其他服务文件相同的访问令牌后面。不存在一次返回多个会话的路由:客户端从一个会话名称派生一个路径并获取该路径。 - 读取api (GET,由服务器端支持)
GH_TOKEN):GET /api/itembody,GET /api/comments,GET /api/prfiles,GET /api/prcommits,GET /api/relatedprs,GET /api/issuetitle. 每个都需要一个url查询参数。GET /api/issuetitle返回{ state, merged, isPullRequest, title },在那里title来自拉取请求摘要的拉取请求url和来自issue的issue url;它通过进程内缓存提供:合并的结果永久缓存,并且在300秒后重新获取其他结果。控制台UI将此端点用于打开的项目标题,并在呈现的markdown中装饰带有打开/关闭/合并状态图标和标题的完整PR/issue URL链接。 - 操作api (POST, JSON主体):
POST /api/review(approve,request_changes,close),POST /api/triage(set_status,set_story,close,close_not_planned,snooze_1hour,snooze_3hours,snooze_6hours,snooze_1day,snooze_2days,snooze_3days,snooze_5days,snooze_1week,snooze_1month),POST /api/intmux(set_intmux). 这些路由是多项目的:每个请求体都携带pjcodeUI当前正在查看的项目的视图(取自UI自己的视图)/projects/{pjcode}URL路径),服务器解析它pjcode到它的GitHub项目URLpjcode → projectUrl下面描述的映射,加载项目的状态和故事选项(惰性地,缓存每个)pjcode),并将操作应用于已解决的项目。没有的请求pjcode,或者pjcode如果没有配置项目URL,则会被HTTP 400拒绝。每一次确认的操作都记录了受影响的操作projectItemId到.done根据已解决的pjcode因此,它从每个标签的服务列表中消失,仅为该项目。一个运行serveWeb因此,Instance为每个配置的项目提供读和写服务。 - 注释api (POST, JSON主体,由服务器端支持)
GH_TOKEN):POST /api/comment发布由请求主体命名的问题或拉取请求的顶级评论url。POST /api/reviewcomment在pull request diff上发布一个行锚定的内联评审评论;它的请求体携带url(拉取请求URL),path(更改后的文件路径),line(正整数diff行号),side(RIGHT对于新一侧的添加或上下文行,LEFT对于旧侧的删除线),和body. 服务器解析所有者、回购和拉取请求号url,获取拉请求头提交sha,并调用GitHubPOST /repos/{owner}/{repo}/pulls/{number}/comments与commit_id设置为该sha。当GitHub拒绝请求时,响应是HTTP 502,并且GitHub错误消息在error字段,以便UI可以显示真正的原因。
这个 .done 类中的每个选项卡持久化排除 .done.json 文件旁边的每个选项卡 list.json 下 consoleDataOutputDir. 该文件永远不能直接服务,因为dot-segment块拒绝包含它的任何路径。它是一个范围为单个再生周期的乐观隐藏存储:每个确认的操作都记录受影响的操作 projectItemId 因此,该项目立即消失,并且计划周期重置每个标签 .done.json 回 { "projectItemIds": [] } 在它重新生成项目之后 list.json 文件。这将存储限制为最多记录一个周期的id,因此它永远不会累积和覆盖重新进入合法可见状态的项。
多项目操作路由通过可选配置 consoleProjects 配置文件中的映射。它映射每个 pjcode 到该项目的GitHub项目URL,以便操作api可以解析每个请求的正确项目。配置的 projectName 总是自动添加到此映射(指向已配置的 projectUrl),因此单项目部署不需要 consoleProjects 进入。当相同的时候 serveWeb 实例为多个项目提供写操作,明确列出每个额外的项目:
consoleAccessToken: '<console access token>'
projectUrl: 'https://github.com/orgs/my-org/projects/1'
projectName: 'my-project'
consoleProjects:
my-project: 'https://github.com/orgs/my-org/projects/1'
other-project: 'https://github.com/orgs/other-org/projects/2'
每个项目的状态和故事选项是在第一次加载时惰性加载的 pjcode ,然后在流程的整个生命周期内进行缓存,因此额外的项目不会增加启动成本。
当控制台服务多个项目,其所有者需要不同的GitHub令牌时,设置 consoleGithubTokens 到pjcode到token的内联映射。当对存储库所有者的请求到达时, serveWeb 找到了 pjcode 其项目URL所有者匹配并使用映射中的相应令牌。当 consoleGithubTokens 缺席或 null,或者当没有pjcode映射到所有者时,整个船队 GH_TOKEN 用作该所有者的回退。中找到pjcode时 consoleProjects 而是缺席或空白的 consoleGithubTokens,则抛出错误,而不是静默返回:
consoleAccessToken: '<console access token>'
projectUrl: 'https://github.com/orgs/my-org/projects/1'
projectName: 'my-project'
consoleProjects:
my-project: 'https://github.com/orgs/my-org/projects/1'
other-project: 'https://github.com/orgs/other-org/projects/2'
consoleGithubTokens:
my-project: '<fine-grained-token-for-my-org>'
other-project: '<fine-grained-token-for-other-org>'
在上面的例子中, my-project的令牌用于 my-org 和 other-project的令牌用于 other-org. 提供的机舱 GH_TOKEN 仍然需要 startDaemon 准备周期;只有控制台HTTP服务器路由通过每个项目令牌映射。
故事门子命令 checkStoryGate, setIssueStoryIfUnset, startSpecificationTask 接受可选的 --configFilePath <path> 选项,用于命名相同的配置文件。每个GitHub请求都使用其问题URL的所有者选择的令牌 consoleProjects 和 consoleGithubTokens. 那个令牌来自 pjcode 谁的项目URL所有者与所有者匹配,然后呢 pjcode的 consoleGithubTokens 条目。这个 GH_TOKEN 值在以下情况下使用 --configFilePath 省略或当没有时 pjcode 映射到所有者。 GH_TOKEN 总是必需的,因为它是退路。当匹配时 pjcode 如果令牌不存在或为空,则命令以代码2退出。丢失或格式错误的文件退出,代码为1。当GitHub速率限制请求时,该命令以代码3退出。它的最后一行是JSON对象 {"rateLimited":true,"resetAt":"<ISO 8601 UTC>"},前面是第一行常见的失败消息。速率限制包括主速率限制、作为HTTP 403或HTTP 429发送的辅助速率限制以及GraphQL RATE_LIMITED 错误。 resetAt 是 x-ratelimit-reset 头读取为epoch秒,否则为 retry-after 头添加到命令收到响应的时间,并且是 null 当两个标头都不存在时。对于GraphQL RATE_LIMITED 错误时,同样的报头规则应用于携带错误的HTTP响应。来电者等待 resetAt 在再次运行该命令之前。其他GitHub请求失败退出代码2。
这个 checkIssueReviewReadiness 子命令允许代理自检某个问题当前是否已准备好审查。它不会改变问题状态字段,也不会发布任何评论。它将一个JSON行写入形状的stdout { "reviewReady": boolean, "rejections": [{ "type": string, "detail": string }] } 如果评估成功,不管是否准备就绪,则退出0;非零退出表示操作错误(认证失败、网络错误)。拒绝类型包括: ISSUE_NOT_FOUND, NO_REPORT_FROM_AGENT_BOT, PULL_REQUEST_NOT_FOUND, PULL_REQUEST_IS_DRAFT, PULL_REQUEST_CONFLICTED, ANY_CI_JOB_FAILED_OR_IN_PROGRESS, REQUIRED_CI_JOB_NEVER_STARTED, ANY_REVIEW_COMMENT_NOT_RESOLVED, MULTIPLE_PULL_REQUESTS_FOUND。这个 --projectUrl Option是可选的;当省略时,该命令仍然只使用问题URL运行。
这个 selectOauthToken 子命令读取的每个令牌速率限制缓存相同 startDaemon 代理写入(参见下面的“Claude OAuth令牌旋转”)并将一个令牌字符串打印到标准输出,以便调用者可以在启动Claude Code之前选择合适的令牌。它是只读的:它永远不会启动代理,永远不会改变任何缓存文件,也永远不会在任何地方写入令牌。选择分为两个阶段。 首先,候选过滤器保留5小时窗口至少有60%空闲(5小时利用率最多为0.40)和7天窗口至少有14%空闲(7天利用率最多为0.86)的令牌,其中“% free”为 1 - utilization. 没有缓存文件的令牌,或者其窗口重置纪元已经过去,在这些检查中被视为完全空闲(利用率0)。过滤器还会排除任何携带未过期响应的令牌 seven_day_fable 拒绝标记-当 fable-该令牌上的模型请求被HTTP 429拒绝(参见下面的“Claude OAuth令牌轮换”)-因为这些交互式会话运行在 fable 模型;没有标记的令牌被视为 fable-可用,并且一旦其存储的重置纪元过去,标记就会被忽略。其次,在幸存的候选者中,它选择其7天窗口重置时间最近的单个令牌(最快重置),因此每周配额将首先被消耗,否则将被重置为未使用;没有活动7天窗口的候选人被视为具有最远的重置(现在+ 7天),因此排序最后。每个令牌条目可以携带一个可选的 selectionWeight (默认为正数 1); 当合格的候选物具有不同的权重时,它们之间的选择就会被该权重随机加权,因此权重较小的令牌被按比例选择的次数较少,而唯一合格的候选物总是被选择,而不管其权重如何。当每个符合条件的候选项共享相同的权重(默认值)时,将不加更改地使用上述确定性的最快重置选择。 选择的令牌字符串被写入标准输出(可管道),每个候选决策跟踪被写入标准输出。当没有令牌通过过滤器时,不会向stdout写入任何内容,并且命令退出非零,并在stderr上显示解释性消息。令牌列表路径来自 --tokenListJsonPath 或者是 CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH 环境变量;缓存目录来自 --cacheDir 或者是 TDPM_RATELIMIT_CACHE_DIR 环境变量,默认为 ${XDG_CACHE_HOME:-~/.cache}/tdpm/ratelimit。
这个 selectLiveSessionOauthToken 子命令为一个人即将开始的新的实时交互式Claude Code会话选择一个令牌,以便并发的交互式会话分布在不同的令牌上,而不是堆叠在同一个令牌上。它加载令牌列表并读取相同的每个令牌速率限制缓存 selectOauthToken,并且它以完全相同的方式是只读的:它永远不会启动代理,永远不会改变任何缓存文件,也永远不会将令牌写入任何地方。它应用一个基本速率限制资格过滤器(5小时窗口至少有25%的空闲时间,7天窗口至少有1%的空闲时间,丢失的缓存文件或过期的窗口被视为完全空闲,以及任何携带未过期反应的令牌 seven_day_fable 排除拒绝标记),并且还排除任何5小时窗口小于的令牌 minFiveHourFreeRatio (默认为60%)免费或其7天窗口少于 minSevenDayFreeRatio (默认14%)免费;这些更严格的门槛可以防止 cl 在一个几乎耗尽的令牌上启动一个新的交互式会话的脚本。它还通过扫描本地Linux主机上正在运行的Claude Code进程来测量每个令牌的当前实时占用情况 /proc 它读取以空分隔的 /proc/<pid>/environ 当过程是Claude Code过程时,取其 CLAUDE_CODE_OAUTH_TOKEN 还有一个会话密钥: CLAUDE_CONFIG_DIR 当设置时,else CLAUDE_CODE_SESSION_ID 设置后,则为进程id。令牌的占用是绑定到它的不同会话键的数量,因此继承一个配置目录或会话id的子进程计数一次,并且是非交互式的 claude -p 两个进程都不携带的进程仍然算作一个会话。流程没有 CLAUDE_CODE_OAUTH_TOKEN (例如api密钥会话)被忽略,并且跳过无法读取环境的进程。在符合条件的令牌中,选择是确定的和重置优先的。每个符合条件的令牌的并发会话限制为 maxConcurrentSessionCount (默认 10),按其可选值进行缩放 selectionWeight (默认为正数 1). 该限制保持在其全部价值,而代币的5小时窗口的免费份额等于或高于此值 fullSpeedFiveHourFreeRatio (默认 0.5),并与该点以下的自由份额成线性比例逐渐变细。在该窗口重置之前,它的上限是空闲的5小时共享可以携带的会话数:空闲份额除以距离重置的剩余小时数(重置未知或已通过的5小时),每小时不超过一个窗口的五分之一,除以 fiveHourShareConsumedPerSessionHour (默认 0.05(一个会话在一小时内使用的5小时窗口的份额),然后四舍五入。极限永远不会下降 1. 例如,一个令牌在其5小时窗口中有68%的空闲时间,在重置之前有4.6小时的空闲时间,其限制为2个,而一个新窗口的限制为4个,因此会话停止堆积在一个令牌上,该令牌的窗口将在重置之前耗尽。5小时的窗口是唯一降低限制的窗口:它是在单个工作时间内耗尽的窗口,并且在飞行途中遇到的会话必须重新启动,这比节省的成本要高。 7天的窗口永远不会降低限额,所以每周的津贴即将到期,将全速耗尽,而不是丢弃未使用的;7天的窗口期是对候选人的命令。在仍在7天窗口内的令牌中选择重置时间最快的令牌,使即将到期的额度在重置前被消耗掉;现场演出次数越少,打破了僵局。所有五个调谐数(maxConcurrentSessionCount, fullSpeedFiveHourFreeRatio, minFiveHourFreeRatio, minSevenDayFreeRatio, fiveHourShareConsumedPerSessionHour)是从全局配置文件中读取的 --fleetConfigFilePath 或者是 TDPM_FLEET_CONFIG 环境变量,在a下 liveSessionOauthTokenSelection 映射;文件省略的键保留其内置值,而不可读的文件或超出范围的值将被报告为错误,而不是被静默忽略。达到其限制的令牌将被跳过,转而使用下一个仍有空间的最快重置令牌,当每个符合条件的令牌都达到其限制时,返回一个会话(活动会话加一个,除以限制)后最少超过其限制的令牌,而不是没有,因此溢出会在令牌之间传播。 无论其重量或占用率如何,始终选择唯一合格候选的令牌。当没有令牌通过主实时会话过滤器(5h >= 60%, 7d >= 14%)时,回退阶段运行:它保留任何未排除的令牌(非订阅禁用,非统一拒绝,非可选拒绝),其7天窗口至少有3%的空闲时间,然后选择其中具有最高5小时空闲比率的令牌(由更少的实时会话打破),因此当所有令牌都空闲时,会话仍然可以开始 接近严格的门槛。选定的令牌字符串被写入标准输出(可管道),每个候选决策跟踪被写入标准输出;如果没有令牌同时通过主过滤器和回退过滤器,则不会向标准输出写入任何内容,并且命令以非零状态退出,并在标准输出上显示解释性消息。令牌列表路径和缓存目录被完全解析为 selectOauthToken. 因为占位是从 /proc/<pid>/environ,此子命令是特定于linux的。
这个 startDaemon 命令支持 preparationWorker 的全局配置文件中的映射 --fleetConfigFilePath 或者是 TDPM_FLEET_CONFIG 环境变量。本节控制准备工作池的每个令牌并发性。支持密钥:
normalConcurrentLimit(默认为正整数6):每个Claude OAuth令牌可以全速运行的并发准备工作的最大数量。当一个令牌的5小时或7天利用率上升到80%以上时,它的每个令牌并发性线性递减normalConcurrentLimit降到1。maxConcurrentWorkers(默认为正整数40):所有令牌上并发运行的准备工作者总数的主机范围上限。一旦这么多工作线程已经在运行,那么无论每个令牌的限制如何,都不会在该守护进程周期中生成新的工作线程。graphqlRateLimitFloor(非负整数,默认值500):在守护进程周期生成任何工作程序之前,必须保留在当前速率限制窗口中的GitHub GraphQL API请求的最小数量。当剩余计数等于或低于此值时,整个刷出步骤将被跳过。
文件省略的键保留其内置值;不可读的文件或超出范围的值被报告为错误,而不是被静默忽略。例子:
preparationWorker:
normalConcurrentLimit: 8
maxConcurrentWorkers: 40
graphqlRateLimitFloor: 500
这个 schedule 命令(触发 schedule)支持 startPreparation 命名的全局配置文件中的映射 TDPM_FLEET_CONFIG 环境变量。支持密钥:
maximumPreparingIssuesCount(整数,默认值至少为180):准备状态下的最大问题数,在项目README和项目配置都没有设置时使用maximumPreparingIssuesCount。llmModelNameByAgentName(代理名到模型名的映射,默认不设置):代理名是映射的一个键并且没有llm-model:标签。在每次调度运行时都会再次读取该文件,因此更改将在下次运行时生效,而无需重新启动守护进程。- 这个
startPreparation.llmModelNameByAgentName值必须是映射。 - 每个条目的值必须是一个非空字符串。值为null的条目将被忽略。
- 工作会话的模型按以下顺序选择
llm-model:标签,然后llmModelNameByAgentName,然后是令牌路由模型,然后是代理定义model:线,然后defaultLlmModelName。
- 这个
文件省略的键保留其内置值;一个 maximumPreparingIssuesCount 值不大于等于1的整数将报告错误。例子:
startPreparation:
maximumPreparingIssuesCount: 80
以地图为例:
startPreparation:
maximumPreparingIssuesCount: 80
llmModelNameByAgentName:
triager: 'claude-haiku-5-5'
developer: 'gpt-5.6-luna'
这个 notifyFinishedIssuePreparation 和 revertOrphanedPreparation 命令支持 workflowIssueReporter 的全局配置文件中的映射 --fleetConfigFilePath 或者是 TDPM_FLEET_CONFIG 环境变量。当静默重新调度升级触发时(代理被调度并且连续三次没有返回报告,这表明是TDPM流程级别的问题,而不是特定于任务的问题),TDPM会自动在配置的存储库中创建跟踪问题,或者,当具有相同标题的开放问题已经存在时,会向其添加注释,而不是打开一个副本。支持密钥:
owner(字符串,必选):创建工作流问题的仓库的GitHub所有者(组织或用户)。repo(字符串,必选):该所有者下的存储库名称。projectUrl(string,可选):添加新创建的问题的GitHub ProjectV2的URL。当项目包含一个故事选项,其名称包含“workflow blocker”(不区分大小写)时,问题的story字段将自动设置为该选项。当这个URL以GitHub org (https://github.com/orgs/{org}/projects/{n})或用户(https://github.com/users/{user}/projects/{n})项目URL(可选地后面跟着/views/N路径后缀或查询参数,如?fullscreen=true),还附加了舰队任务创建控制台链接?projects={org}/{n}或?projects={user}/{n}(除了…之外)assignees={owner}),以便从该链接创建的任何新问题都会自动添加到项目中。
例子:
workflowIssueReporter:
owner: my-org
repo: workflow-issues
projectUrl: https://github.com/orgs/my-org/projects/5
这个 startDaemon, notifyFinishedIssuePreparation, checkIssueReviewReadiness, schedule (trigger=schedule)命令支持顶层 errorReportingRepository 全局配置文件中的字符串 --fleetConfigFilePath 或者是 TDPM_FLEET_CONFIG 环境变量。当设置时,它优先于每个项目 errorReportingRepository config key并将所有tdpm级别的错误问题路由到该单个存储库:(1)任何未处理的错误逃避CLI进程,在那里创建一个新的GitHub问题,或在现有的开放问题上添加相同标题的注释;(2)“注册缺少代理定义”任务和“工作流配置中未注册的代理”阻塞问题在那里创建,而不是在产品存储库中创建。原始TDPM项目名称包含在拦截器问题主体中(as - TDPM project: <name>)当… projectName 在每个项目配置文件中配置,因此即使所有错误都路由到中央存储库,源项目也是可识别的。需要 GH_TOKEN。
例子:
errorReportingRepository: my-org/secretary
这个 countInTmuxByHumanSessionsPerToken 子命令报告,每个Claude Code OAuth令牌,有多少在该令牌下运行的实时交互会话属于当前处于GitHub项目状态的问题 In Tmux by human. 它是只读的:它永远不会启动代理,永远不会改变任何缓存文件,也不会在任何地方写入任何令牌。它通过扫描本地Linux主机上正在运行的进程来枚举实时交互会话 /proc 它读取以空分隔的 /proc/<pid>/cmdline 并只选择为交互式会话启动的进程,会话由标识 --name <issue-url> 参数,其值为HTTP或HTTPS URL;进程与 -p 或 --print 论点被排除在外。对于每个选定的进程,它读取 /proc/<pid>/environ 然后把它 CLAUDE_CODE_OAUTH_TOKEN 和 CLAUDE_CODE_SESSION_ID;缺少其中之一的进程将被跳过,而无法读取文件的进程将被跳过。然后,它加载项目的问题及其状态(重用守护进程使用的相同的磁盘上问题缓存),只保留具有 --name issue URL映射到Status中的open issue In Tmux by human,并计算不同的 CLAUDE_CODE_SESSION_ID 每个令牌值,因此继承一个会话id的子进程计数一次。它将每个令牌的一个制表符分隔行写入表单中的stdout <tokenName>\t<count> (令牌名称来自令牌列表;原始令牌值永远不会打印),并将每次运行的决策跟踪写入stderr。令牌列表路径来自 --tokenListJsonPath, claudeCodeOauthTokenListJsonPath 配置值,或 CLAUDE_CODE_OAUTH_TOKEN_LIST_JSON_PATH 环境变量。因为占位是从 /proc/<pid>/cmdline 和 /proc/<pid>/environ,此子命令是特定于linux的。
这个 killTmuxSession 子命令通过运行终止tmux会话 tmux kill-session 同时停止每个会话的会话 cl-*.scope systemd --user 单元,巩固逻辑,否则需要由调用者手工编写。它只需要两个相互排斥的选项中的一个。 --session <name> 杀死另一个命名会话:它首先停止会话 cl-*.scope 单元(包装) systemctl --user stop 电话 systemctl --user reset-failed 之前和之后),然后运行 tmux kill-session -t "=<name>" 使用确切的名称 = 前缀,这样一个会话名称本身就是另一个会话名称的前缀,永远不会匹配错误。 --self 从内部终止当前会话:因为活动会话无法运行 tmux kill-session 在自己的作用域停止步骤完成之前不被终止,此模式派生调用方自己的 cl-*.scope 单位名称 /proc/self/cgroup 并且只停止该作用域,而不调用 tmux kill-session 在所有。任何一个选项都不能与另一个选项组合,提供任何一个选项都是错误的。
这个 ownerCallFileAppend 和 ownerCallFileDelete 子命令为每个tmux会话维护一个临时文件,保存所有者调用所引发的会话,并且所有者尚未应答。两者都只接受tmux会话名称,并从单个规则派生文件位置 call-to-user/{pjcode or NA}/{session key}.yaml 下 --inTmuxDataDir,其中会话密钥是会话名称的派生,in-tmux子命令已经应用于issue url(每一个) . 和 : 由下划线替换),然后替换每一个 / 用下划线表示。该派生为原始问题url和它的tmux会话名称提供了相同的键,因此知道会话名称的写入端和知道问题url的读取端到达相同的文件,并且永远不会偏离;这是同样的规则 serveWeb 上述路由解析。两个子命令都不接受项目代码: ownerCallFileAppend 从人工输入的数据中解析它 --inTmuxDataDir,它能容纳一个 {pjcode}.v4.json 每个项目列出了该项目的会话,其会话列表包含会话的项目给出了代码。 NA 是没有项目列表的会话的项目代码,例如不绑定到一个项目的长时间运行的会话。 ownerCallFileAppend 每个调用附加一个YAML文档,由YAML自己的文档分隔 --- 文档分隔符,在文件不存在时创建该文件,以便最早的调用保持在第一位;每个文档重复 sessionName 因此,阅读器可以拒绝不属于它打开的会话的文件 calledAt 作为UTC ISO-8601,具有秒精度和尾随 Z,并携带 body 作为带有显式缩进指示符的文字块 2 因此,第一行以空格开头的主体仍然是有效的YAML。身体被传递为 --body-file 而不是作为参数,因为调用体是多行并且可能很长。 ownerCallFileDelete 在保存该会话的项目目录下删除该会话的文件,因此,在附加其调用后在项目之间移动的会话不会留下任何东西,并且在文件已经缺席时成功退出,因此运行两次的重置不是错误。成功时,两个子命令都不会向stdout写入任何内容。预定的运行还会删除每个问题已关闭的会话的文件。
这个 archive-unresumable-session 子命令将由于会话提示超出模型上下文限制而结束的会话的存储会话记录移到一边,因此该任务的下一次启动将启动一个新的会话,而不是恢复一个永远无法再次加载的记录。如果没有它,每次尝试都恢复相同存储会话的启动程序将在相同的条件下终止,并且任务永远不会启动。它读取已完成运行的流json日志 --log-file 逐行解析作为JSON对象的每一行,并忽略非JSON对象的每一行。一个记录匹配所有三个 type = result, is_error 是 true, result = Prompt is too long 持有;记录被归档的会话是 session_id 匹配的记录。仅达到使用限制的会话以。结束 terminal_reason api_error, api_error_status 429,而另一个 result,所以它不匹配,没有东西移动。当没有匹配的记录时,子命令写入 no-op stdout,不更改任何内容,并退出0。当一个记录匹配时,它创建 --archive-dir 如果不存在,就移动 <--session-dir>/<session-id>.jsonl 来 <--archive-dir>/<session-id>.jsonl写道, archived <destination path> 标准输出,并退出0。对话记录是移动的,而不是删除的,所以对话是可恢复的。当一条记录匹配,但该会话记录文件不存在时,子命令会写入一个诊断,将预期的源路径命名为stderr,并退出1。它只改变记录的存储位置;它不会改变为任务选择会话的方式。
这个 select-resumable-session 子命令决定被调度的工人是否恢复早先的工作 claude 同一任务和代理的会话,以及它传递给哪个会话id claude --resume <session id>,因此工作启动器只收集目录路径并将答案传递给 claude. 脚本文件是JSON行文件 <session id>.jsonl 在这 claude 存储一个会话:会话目录中的常规文件,名称以。结尾 .jsonl 而不是以 .;丢失的会话目录不包含记录文件。只有当转录文件的第一行包含 "customTitle":"<--session-name>",包括结束引号,所以只以名字开头的标题不是候选人。候选人是不可恢复的,当 <--archive-root>/<session id>.jsonl 或 <--archive-root>/<directory>/<session id>.jsonl 存在,或者它的最后一个主链辅助条目是一个API错误,其文本为 Prompt is too long. 一个主链助手条目是一个JSON行 type assistant 和 isSidechain 不 true;它的错误文本是 text 的值 message.content 项目连接在一起时 isApiErrorMessage 是 true,否则为空;非JSON对象的行将被忽略。在 --session-dir 选择具有最新修改时间的可恢复候选文件,并且在具有相同修改时间的候选文件中,文件名按升序排在第一位的候选文件获胜。当 --session-dir 不产生任何选择,所有的成绩单文件 --other-session-dir 目录根据修改时间排列在一起:在具有相同修改时间的候选目录中,首先给出的目录中的文件名优先,在一个目录中,按升序顺序首先出现的文件名优先。一个目录等于 --session-dir ,以及文件名也存在于?中的成绩单文件 --session-dir 是跳过。所选的成绩单文件必须有一个输入时间,第一个JSON对象行带有一个字符串 timestamp 解析为日期;如果没有,子命令将写入 Session resumption: <session id> has no entry time to compare with the definitions; starting a new session 看不懂,不复制,不打印。从另一个会话目录中选择的记录文件被复制到 --session-dir,它是用mode创建的 700 当不存在时:副本保留源的模式及其访问和修改时间,写为 <session id>.jsonl.copying,并重命名为 <session id>.jsonl,源头就会留在原地。副本文件复制完成后,其会话id目录 <session id>/ 源成绩单文件保存了该会话的子代理成绩单和工具结果,它的旁边是递归复制的,保留了它的时间、它存在的时间和 <--session-dir>/<session id> 没有。子命令然后写入 Session resumption: copied <source transcript file> into <--session-dir> (original kept) 绊倒,在…之前 Session resumption: warning: could not copy session directory <source session id directory> into <--session-dir> 当只有会话id目录复制失败时,仍然会恢复会话。当成绩单文件复制失败时,或 <--session-dir>/<session id>.jsonl 已经存在,它写道 Session resumption: could not copy <source transcript file> into <--session-dir>; starting a new session 踉踉跄跄,什么也不印。标准输出带有要恢复的会话id,后跟一个换行符,或者当工作线程启动一个新的会话时不带任何内容 claude 会话,在每种情况下退出码都是0;缺失的必选选项以非零形式存在。
这个 check-issue-silent-dispatch-allowed 子命令报告是否由 --issue-url 在其自己的主体中声明,期望以没有代理注释结束的分派周期,并且不能将其视为丢失的报告。这让 notifyFinishedIssuePreparation 跳过 NO_REPORT_FROM_AGENT_BOT 拒绝一个任务,该任务的主体已经指示分配的代理在某些或所有调度周期中保持沉默,这种设计用于某些重复的、所有者导向的任务。它解析 owner, repo,以及来自的发行号 --issue-url,从GitHub REST API中获取该问题的主体 GH_TOKEN 环境变量,并根据确切的文字标记对其求值 <!-- TDPM_SILENT_DISPATCH_ALLOWED -->标记必须作为正文的连续子字符串出现,因此没有其 <!-- --> 包装不算数。它不向stdout写入任何内容。当标记存在时退出0,当标记不存在时退出1,当出现错误时退出2 --issue-url 无法解析或GitHub API调用失败。
这个 workerRequestTextRead 子命令打印工作会话收到的由。命名的问题的请求文本 --issueUrl,完全 Take ownership of <issue url> 后跟一个换行符,to stdout,不向stderr写入任何内容,并退出0。worker启动器调用它,并将打印的文本原原本本地传递给worker会话,因此该文本在该CLI中的一个地方定义,该CLI通过精确的方式标识正在运行的worker进程 -p 或 --print 论点。它不会读取任何内容,也不会改变任何内容。当 --issueUrl 是缺失的,它什么都不打印到标准输出,打印一个错误的命名 --issueUrl 错误,并退出非零。
例子📖
这里有一个简单的例子来说明它的用法:
npx github-issue-tower-defence-management schedule -t schedule -c ./config.yml
npx github-issue-tower-defence-management schedule -t issue -c ./config.yml -i https://github.com/HiromiShikata/test-repository/issues/1
npx github-issue-tower-defence-management startDaemon --configFilePath ./preparator-config.yml
npx github-issue-tower-defence-management notifyFinishedIssuePreparation --configFilePath ./preparator-config.yml --issueUrl https://github.com/HiromiShikata/test-repository/issues/1
npx github-issue-tower-defence-management checkIssueReviewReadiness --configFilePath ./preparator-config.yml --issueUrl https://github.com/HiromiShikata/test-repository/issues/1
npx github-issue-tower-defence-management serveWeb --configFilePath ./preparator-config.yml --port 9980
TOKEN=$(npx github-issue-tower-defence-management selectOauthToken --tokenListJsonPath ./claudeCodeOauthTokenList.json)
TOKEN=$(npx github-issue-tower-defence-management selectLiveSessionOauthToken --tokenListJsonPath ./claudeCodeOauthTokenList.json)
npx github-issue-tower-defence-management countInTmuxByHumanSessionsPerToken --configFilePath ./preparator-config.yml --tokenListJsonPath ./claudeCodeOauthTokenList.json
REQUEST_TEXT=$(npx github-issue-tower-defence-management workerRequestTextRead --issueUrl https://github.com/HiromiShikata/test-repository/issues/1)
配置
Schedule命令配置
这个 config.yaml 为 schedule 的输入类型必须匹配 HandleScheduledEventUseCase.run(). 结构如下:
工作流状态名称(Unread, Awaiting Workspace, Awaiting Owner, Preparation, Failed Preparation, Todo by human, In Tmux by human, In Tmux by agent, Done, Icebox)是固定的代码常量,不能从CLI选项、配置文件或项目README中重写。这个 schedule 命令在每次运行时自动在目标项目上创建任何缺失的必需状态 SetupTowerDefenceProjectUseCase. 具有遗产的项目 Todo 和 In Tmux 状态名自动迁移到 Todo by human 和 In Tmux by human 分别通过重用现有的选项id,以便保留任务关联。遗留 PC Todo 状态将从所需列表中删除,并在下一次安装运行时从项目状态列表中排除。遗留 Awaiting Task Breakdown 从所需列表中删除Status;处于该状态的任何项目项都会自动移动到 Todo by human 并且在下一次安装运行时删除状态选项。这个 Awaiting Owner Status是必需的工作流状态。
这两种与tmux相关的状态具有不同的含义。 In Tmux by human 表示在实时tmux会话中与人类所有者一起处理的任务,人类所有者参加会话并与会话进行对话;所有者确实会查看这些任务。 In Tmux by agent 表示由tmux中的代理管理的任务,而人类所有者不会查看该任务。因此,启动到一个实时的、所有者参与的会话中的任务属于 In Tmux by human,并设定这样一个任务 In Tmux by agent 会让它从所有者的视线中消失。
disabled: boolean # When true, skip all processing and return null
org: string # Organization name
projectUrl: string # URL of the target project
manager: string # GitHub account name of the manager
workingReport:
repo: string # Repository name
members: # Array of member's GitHub account names
- string
- string
warningThresholdHour?: number # Optional: Warning threshold in hours
spreadsheetUrl: string # URL of the Google Spreadsheet
reportIssueTemplate?: string # Optional: Template for issue reports
reportIssueLabels: # Array of issue labels
- string
- string
startPreparation?: # Optional: Enable automatic issue preparation workflow
defaultAgentName: string # Default agent name to assign for preparation
configFilePath: string # Path to config file passed to the aw command
defaultLlmModelName?: string | null # Optional: Default LLM model name (overridable via llm-model: label and llmModelNameByAgentName)
defaultLlmAgentName?: string | null # Optional: Default LLM agent name (overridable via llm-agent: label)
maximumPreparingIssuesCount: number | null # Max concurrent preparing issues. When token rotation is active, effective concurrency is also capped at 6 per available token. When null, the default is 6 per available token, or 6 without token rotation
utilizationPercentageThreshold?: number # Optional: 5-hour utilization hard threshold (percentage, default 90). Tokens at or above this value are excluded from rotation
allowedIssueAuthors?: string[] | null # Optional: Only start preparation for issues from these authors (null or omitted = no authors allowed; set an explicit list)
preparationProcessCheckCommand?: string # Optional: Shell command template with {URL} placeholder to check if a preparation process is alive. When set, orphaned Preparation issues (process exits non-zero, or stale aw log) are evaluated for completion: if work is done they advance to Awaiting Owner; otherwise they fall back to Awaiting Workspace.
awaitingOwnerStatus?: string | null # Optional: Project status name for issues awaiting owner review. When set with preparationProcessCheckCommand, orphaned issues with no rejections advance to this status instead of awaitingWorkspaceStatus
autoAdvanceQualityCheckEnabled?: boolean # Optional: When true, issues in the Awaiting Owner status are automatically advanced to Done on each scheduled cycle. Default false (issues remain in Awaiting Owner for human review). Use awaitingOwnerStatus to configure the status name when it differs from the default
autoRevertReopenedDoneEnabled?: boolean # Optional: When true, issues in the Done status that were reopened on GitHub (stateReason = REOPENED) are automatically moved back to Awaiting Workspace on each scheduled cycle. Default false. Requires startPreparation to be configured.
codexHomeCandidates?: string[] | null # Optional: Ordered list of CODEX_HOME directory paths. Each launched Codex job cycles through the list; absent or empty keeps current behavior
awLogDirectoryPath?: string # Optional: Directory path where aw log files named {org}_{repo}_{number}_* are written. Used with awLogStaleThresholdMinutes to detect zombie-wrapper orphans, and by startPreparation when an issue has more than one open same-repository PR: the PR whose head branch is checked out in the working directory recorded on the `Current directory:` line of the issue's newest {org}_{repo}_{number}_{YYYYMMDD}_{HHMMSS}.log is adopted and every other PR is closed. When this is unset, or that branch is the head branch of no open PR, the PR created first is adopted
awLogStaleThresholdMinutes?: number # Optional: Minutes since last aw log mtime after which a Preparation issue is considered orphaned even when pgrep still returns 0 (outer wrapper alive but inner claude dead). Requires awLogDirectoryPath
agentDefinitionsDirectoryPath?: string # Optional: Directory path containing per-agent definition markdown files named {agentName}.md, each with a YAML frontmatter `model:` field. Used as a model source when neither an `llm-model:` label, an `llmModelNameByAgentName` entry, nor live-session token routing resolves a model. Defaults to ~/.claude/agents when unset
labelsAsLlmAgentName?: string[] | null # Optional: List of issue labels that are themselves agent names. When an issue carries any label that is included in this list, that label name is used as the agent name. Selection preced