triflux 10.50.0 → 10.50.2
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/CLAUDE.md +5 -5
- package/README.ko.md +5 -6
- package/README.md +5 -6
- package/bin/tfx-live.mjs +0 -47
- package/bin/triflux.mjs +3 -72
- package/cto/collect.mjs +16 -174
- package/cto/current.schema.json +4 -16
- package/cto/events.mjs +13 -294
- package/cto/hygiene.mjs +26 -346
- package/cto/index.mjs +5 -25
- package/cto/lake-root.mjs +114 -16
- package/cto/status.mjs +31 -78
- package/hub/lib/cto-env.mjs +0 -23
- package/hub/paths.mjs +0 -6
- package/hub/pipeline/index.mjs +0 -190
- package/hub/pipeline/transitions.mjs +0 -10
- package/hub/reflexion.mjs +1 -84
- package/hub/store-adapter.mjs +1 -1
- package/hub/team/conductor.mjs +1 -1
- package/hub/team/execution-mode.mjs +1 -1
- package/hub/team/headless.mjs +1 -1
- package/hub/team/psmux.mjs +16 -402
- package/hub/team/session.mjs +1 -28
- package/hub/team/terminal-opener.mjs +11 -35
- package/hud/cli-policy.mjs +1 -5
- package/hud/colors.mjs +0 -13
- package/hud/constants.mjs +4 -135
- package/hud/context-monitor.mjs +3 -64
- package/hud/hud-qos-status.mjs +43 -225
- package/hud/providers/claude.mjs +4 -16
- package/hud/providers/gemini.mjs +7 -698
- package/hud/renderers.mjs +40 -294
- package/hud/terminal.mjs +1 -63
- package/hud/utils.mjs +0 -24
- package/package.json +2 -4
- package/scripts/__tests__/release-governance.test.mjs +0 -1
- package/scripts/lib/gemini-profiles.mjs +1 -1
- package/scripts/lib/legacy-mcp-cleanup.mjs +14 -3
- package/scripts/lib/psmux-info.mjs +18 -49
- package/scripts/lib/skill-template.mjs +0 -26
- package/scripts/pack.mjs +2 -21
- package/scripts/release/bump-version.mjs +8 -31
- package/scripts/release/lib.mjs +6 -13
- package/scripts/release/prepare.mjs +1 -5
- package/scripts/setup.mjs +26 -30
- package/scripts/tfx-route-post.mjs +0 -1
- package/scripts/tfx-route.sh +0 -9
- package/skills/tfx-auto/SKILL.md +1 -3
- package/skills/tfx-ship/SKILL.md +1 -1
- package/bin/tfx-profile.mjs +0 -7
- package/cto/dashboard.mjs +0 -474
- package/cto/hygiene-actions.mjs +0 -355
- package/cto/hygiene-notify.mjs +0 -170
- package/cto/steward.mjs +0 -362
- package/hub/codex-compat.mjs +0 -12
- package/hub/fullcycle.mjs +0 -113
- package/hub/gemini-adapter.mjs +0 -174
- package/hub/intent.mjs +0 -321
- package/hub/lib/mcp-response-cache.mjs +0 -239
- package/hub/lib/path-utils.mjs +0 -165
- package/hub/lib/timeline-adapter.mjs +0 -190
- package/hub/log-retention.mjs +0 -726
- package/hub/pipeline/gates/confidence.mjs +0 -75
- package/hub/pipeline/gates/consensus.mjs +0 -106
- package/hub/pipeline/gates/index.mjs +0 -9
- package/hub/pipeline/gates/selfcheck.mjs +0 -120
- package/hub/quality/deslop.mjs +0 -274
- package/hub/research.mjs +0 -155
- package/hub/role-control-events.mjs +0 -113
- package/hub/routing/complexity.mjs +0 -269
- package/hub/routing/index.mjs +0 -122
- package/hub/routing/q-learning.mjs +0 -387
- package/hub/token-mode.mjs +0 -228
- package/hub/workers/gemini-worker.mjs +0 -217
- package/hud/mission-board.mjs +0 -56
- package/hud/providers/cto.mjs +0 -76
- package/references/cli-parameter-reference.md +0 -240
- package/scripts/__tests__/gen-skill-manifest.test.mjs +0 -81
- package/scripts/__tests__/skill-template.test.mjs +0 -293
- package/scripts/__tests__/smoke.test.mjs +0 -26
- package/scripts/__tests__/tfx-route-bash-node-parity.test.mjs +0 -223
- package/scripts/__tests__/tfx-route-node-entry.test.mjs +0 -449
- package/scripts/__tests__/tfx-route-phase1-modules.test.mjs +0 -268
- package/scripts/cache-buildup.mjs +0 -30
- package/scripts/cli-route.sh +0 -3
- package/scripts/gen-skill-manifest.mjs +0 -87
- package/scripts/lib/agent-json.mjs +0 -27
- package/scripts/lib/async.mjs +0 -174
- package/scripts/lib/cli-agy.mjs +0 -85
- package/scripts/lib/cli-codex.mjs +0 -62
- package/scripts/lib/cli-gemini.mjs +0 -37
- package/scripts/lib/env.mjs +0 -14
- package/scripts/lib/hub.mjs +0 -48
- package/scripts/lib/pid.mjs +0 -63
- package/scripts/lib/quota.mjs +0 -47
- package/scripts/lib/skill-state.mjs +0 -220
- package/scripts/lib/team.mjs +0 -75
- package/scripts/lib/timeout.mjs +0 -66
- package/scripts/lib/tmp.mjs +0 -26
- package/scripts/lib/toml.mjs +0 -132
- package/scripts/preinstall.mjs +0 -128
- package/scripts/session-spawn-helper.mjs +0 -261
- package/scripts/test-tfx-route-no-claude-native.mjs +0 -63
- package/scripts/tfx-batch-stats.mjs +0 -117
- package/scripts/tfx-route.mjs +0 -314
- package/scripts/token-snapshot.mjs +0 -696
- package/skills/tfx-auto/skill.json +0 -5
- package/skills/tfx-doctor/skill.json +0 -5
- package/skills/tfx-harness/skill.json +0 -5
- package/skills/tfx-live/skill.json +0 -5
- package/skills/tfx-remote/skill.json +0 -5
- package/skills/tfx-research/skill.json +0 -6
- package/skills/tfx-review/skill.json +0 -6
- package/skills/tfx-setup/skill.json +0 -5
- package/skills/tfx-ship/skill.json +0 -5
- package/skills/tfx-wt/skill.json +0 -5
- package/tui/codex-profile.mjs +0 -421
- package/tui/gemini-profile.mjs +0 -286
package/CLAUDE.md
CHANGED
|
@@ -28,14 +28,14 @@ macOS/Linux는 플랫폼 보호기가 아무 작업도 하지 않게 처리하
|
|
|
28
28
|
|
|
29
29
|
위 `<psmux-wt>` 룰셋은 Windows 전용이다. macOS/Linux 환경에서 triflux가 터미널/세션을 다루는 방식.
|
|
30
30
|
|
|
31
|
-
### terminal-opener.mjs 3단계 대체 경로(`hub/team/terminal-opener.mjs
|
|
31
|
+
### terminal-opener.mjs 3단계 대체 경로(`hub/team/terminal-opener.mjs` 의 `openCommand`)
|
|
32
32
|
|
|
33
33
|
`openCommand()` 가 순차 평가하는 분기:
|
|
34
34
|
|
|
35
35
|
| 우선순위 | 조건 | 동작 | API |
|
|
36
36
|
|---------|------|------|-----|
|
|
37
37
|
| 1 | `platform === "win32"` | `wt-manager.createTab` | `hub/team/wt-manager.mjs` |
|
|
38
|
-
| 2 | `isTmuxLikeMux(mux
|
|
38
|
+
| 2 | `isTmuxLikeMux(mux)` (`detectMultiplexer()` → `getMultiplexerType()`/`hasMultiplexer()`/`hasTmux()`) | `tmux new-window -n <title> <command>` | 셸 직접 호출 |
|
|
39
39
|
| 3 | `platform === "darwin"` (대체 경로) | `open -a Terminal` | macOS `open` 명령 |
|
|
40
40
|
| — | Linux(멀티플렉서 없음) | 미지원(`false` 반환) | — |
|
|
41
41
|
|
|
@@ -43,9 +43,9 @@ macOS/Linux는 플랫폼 보호기가 아무 작업도 하지 않게 처리하
|
|
|
43
43
|
|
|
44
44
|
| 후보 | 필요성 | 이유 |
|
|
45
45
|
|------|--------|------|
|
|
46
|
-
| iTerm2 관리자 | **불필요** | `hub/lib/env-detect.mjs:96`이 `TERM_PROGRAM === "iTerm.app"`을 감지하지만 별도 GUI 패인 조작은 tmux
|
|
47
|
-
| tmux 관리자 | **불필요** |
|
|
48
|
-
| psmux 관리자 | **이미 있음** | `hub/team/psmux.mjs`가
|
|
46
|
+
| iTerm2 관리자 | **불필요** | `hub/lib/env-detect.mjs:96`이 `TERM_PROGRAM === "iTerm.app"`을 감지하지만 별도 GUI 패인 조작은 tmux로 처리한다. 새 창은 `open -a Terminal` 대체 경로로 충분하다. |
|
|
47
|
+
| tmux 관리자 | **불필요** | `terminal-opener.mjs`가 `tmux new-window`로 직접 호출한다. |
|
|
48
|
+
| psmux 관리자 | **이미 있음** | `hub/team/psmux.mjs`가 Windows에서는 psmux, macOS/Linux에서는 tmux를 사용한다. |
|
|
49
49
|
|
|
50
50
|
참고 사례: OMC(`oh-my-claudecode`)도 운영체제별 관리자를 만들지 않고 Tmux 관리자·워크트리 관리자·Claude 실행기 세 구성 요소로 정리한다. 이 저장소 결정의 외부 근거가 아니라 비교 참고로만 본다.
|
|
51
51
|
|
package/README.ko.md
CHANGED
|
@@ -131,12 +131,11 @@ Codex는 이름 붙은 프로필로 실행한다. 모델 ID는 `~/.codex/<프로
|
|
|
131
131
|
| `tfx hub` | 로컬 Hub: `start`, `stop`, `status`, `ensure` |
|
|
132
132
|
| `tfx mcp` | 관리형 MCP 레지스트리: `list`, `sync`, `add`, `remove` |
|
|
133
133
|
| `tfx handoff` | 현재 맥락을 다른 세션이나 호스트로 넘길 프롬프트로 묶음 |
|
|
134
|
-
| `tfx cto` | 저장소 단위 권위 콘솔: `collect`, `status`, `
|
|
134
|
+
| `tfx cto` | 저장소 단위 권위 콘솔: `collect`, `status`, `hygiene`(dry-run) |
|
|
135
135
|
| `tfx review` / `tfx codex-team` | Codex git diff 리뷰 / Codex 주도 팀 모드 |
|
|
136
136
|
| `tfx stealth-fetch <url>` | cloakbrowser로 URL 하나를 가져옴(JSON을 stdout으로) |
|
|
137
137
|
| `tfx notion-read`, `tfx why`, `tfx schema`, `tfx list`, `tfx monitor`, `tfx update`, `tfx version` | Notion → Markdown, 커밋 intent 트레일러, CLI 스키마, 설치된 스킬, TUI 모니터, 업데이트, 버전 |
|
|
138
138
|
| `tfx-live` | 라이브 세션 브리지(`/tfx-live` 스킬과 같은 명령) |
|
|
139
|
-
| `tfx-profile` | 대화형 Codex 프로필 관리자 |
|
|
140
139
|
|
|
141
140
|
정확한 인자는 `tfx <명령> --help`나 `tfx schema <명령>`으로 확인한다.
|
|
142
141
|
|
|
@@ -168,10 +167,10 @@ Claude Code Bash 도구의 600초 제한에 걸리지 않는다. 이후 `--job-s
|
|
|
168
167
|
다른 경로로 넘어가지 않고 실패한다. 자세한 내용은
|
|
169
168
|
[`.claude/rules/tfx-machine-profile.md`](.claude/rules/tfx-machine-profile.md).
|
|
170
169
|
|
|
171
|
-
**CTO lake.** `tfx cto
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
([ADR-
|
|
170
|
+
**CTO lake.** `tfx cto collect`로 `.triflux/lake/`의 저장소 스냅샷을 갱신하고,
|
|
171
|
+
`tfx cto status`로 생성 시각과 경과 시간을 확인한다. `tfx cto hygiene --dry-run`는 dry-run 결과를
|
|
172
|
+
보고한다. 트레이와 쓰이지 않는 CTO 운영 명령은 제거했다
|
|
173
|
+
([ADR-0024](docs/adr/0024-cto-explicit-queries-only.md)). 자동 수집은 기본으로 꺼져 있고 `TFX_CTO_AUTO_COLLECT=1` 로 켠다([ADR-0018](docs/adr/0018-cto-auto-behaviors-opt-in.md)).
|
|
175
174
|
|
|
176
175
|
**원격 호스트.** `/tfx-remote`와 `--remote <host>`는 `~/.config/triflux/hosts.json`
|
|
177
176
|
(Windows는 `%APPDATA%\triflux\hosts.json`)에서 호스트를 읽는다. `/tfx-remote setup`으로 호스트를
|
package/README.md
CHANGED
|
@@ -132,12 +132,11 @@ escalation chain: [`.claude/rules/tfx-escalation-chain.md`](.claude/rules/tfx-es
|
|
|
132
132
|
| `tfx hub` | Local Hub: `start`, `stop`, `status`, `ensure` |
|
|
133
133
|
| `tfx mcp` | Managed MCP registry: `list`, `sync`, `add`, `remove` |
|
|
134
134
|
| `tfx handoff` | Serialize the current context for another session or host |
|
|
135
|
-
| `tfx cto` | Repo-local authority console: `collect`, `status`, `
|
|
135
|
+
| `tfx cto` | Repo-local authority console: `collect`, `status`, `hygiene` (dry-run) |
|
|
136
136
|
| `tfx review` / `tfx codex-team` | Codex git-diff review / Codex-led team mode |
|
|
137
137
|
| `tfx stealth-fetch <url>` | Fetch one URL through cloakbrowser (JSON on stdout) |
|
|
138
138
|
| `tfx notion-read`, `tfx why`, `tfx schema`, `tfx list`, `tfx monitor`, `tfx update`, `tfx version` | Notion → Markdown, commit intent trailers, CLI schemas, installed skills, TUI monitor, update, version |
|
|
139
139
|
| `tfx-live` | Live session bridge (same as the `/tfx-live` skill) |
|
|
140
|
-
| `tfx-profile` | Interactive Codex profile manager |
|
|
141
140
|
|
|
142
141
|
`tfx <command> --help` and `tfx schema <command>` print the exact arguments.
|
|
143
142
|
|
|
@@ -169,10 +168,10 @@ the `claude agents` panel unless you pass `--no-native-bridge-ui`.
|
|
|
169
168
|
removes a CLI from routing; if no allowed CLI is available the route fails instead of silently
|
|
170
169
|
falling back. Details: [`.claude/rules/tfx-machine-profile.md`](.claude/rules/tfx-machine-profile.md).
|
|
171
170
|
|
|
172
|
-
**CTO lake.** `tfx cto`
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
([ADR-
|
|
171
|
+
**CTO lake.** Run `tfx cto collect` to refresh the repo snapshot in `.triflux/lake/`, then
|
|
172
|
+
`tfx cto status` to inspect it with its generation time and age. `tfx cto hygiene --dry-run` reports
|
|
173
|
+
dry-run findings. The tray and unused CTO operating commands were removed
|
|
174
|
+
([ADR-0024](docs/adr/0024-cto-explicit-queries-only.md)). Automatic collection stays off unless `TFX_CTO_AUTO_COLLECT=1` is set ([ADR-0018](docs/adr/0018-cto-auto-behaviors-opt-in.md)).
|
|
176
175
|
|
|
177
176
|
**Remote hosts.** `/tfx-remote` and `--remote <host>` read hosts from `~/.config/triflux/hosts.json`
|
|
178
177
|
(Windows: `%APPDATA%\triflux\hosts.json`). Run `/tfx-remote setup` to add one, then
|
package/bin/tfx-live.mjs
CHANGED
|
@@ -14,8 +14,6 @@ import { homedir, tmpdir } from "node:os";
|
|
|
14
14
|
import { join as pathJoin, resolve as pathResolve } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import { promisify } from "node:util";
|
|
17
|
-
import { runHygiene } from "../cto/hygiene.mjs";
|
|
18
|
-
import { notifyCtoHygieneOnce } from "../cto/hygiene-notify.mjs";
|
|
19
17
|
import {
|
|
20
18
|
codexThreadNames,
|
|
21
19
|
readCodexSessionRecords,
|
|
@@ -26,7 +24,6 @@ import {
|
|
|
26
24
|
findClaudeTranscript,
|
|
27
25
|
readClaudeTranscript,
|
|
28
26
|
} from "../hub/team/claude-transcript.mjs";
|
|
29
|
-
import { createNotifier } from "../hub/team/notify.mjs";
|
|
30
27
|
import {
|
|
31
28
|
escapePwshSingleQuoted as escapeRemotePwshSingleQuoted,
|
|
32
29
|
probeRemoteEnv as probeRemoteHostEnv,
|
|
@@ -100,8 +97,6 @@ function usage(command) {
|
|
|
100
97
|
" tfx-live orchestrate --task TEXT [--mode peer|codex-led|claude-led] [--codex-transport exec|app-server-uds] [--codex-socket PATH|default] [--cwd DIR] [--timeout 120]",
|
|
101
98
|
" Codex TUI may use a shared app-server daemon. UDS ask resumes the thread to receive answer events; sending to an active thread with --if-busy steer merges input into that turn.",
|
|
102
99
|
" Runs the Claude(UDS)+Codex orchestration engine. --codex-transport app-server-uds drives a real `codex app-server` over WebSocket-over-UDS (experimental); default exec keeps the codex stdio one-shot path.",
|
|
103
|
-
" tfx-live cto-hygiene-notify --root DIR --state-file PATH [--json]",
|
|
104
|
-
" One-shot CTO hygiene dry-run notification: sends only when actionable hygiene state changes; no polling or apply/steward lock.",
|
|
105
100
|
];
|
|
106
101
|
if (!command) return lines.join("\n");
|
|
107
102
|
const selected = lines.filter(
|
|
@@ -4141,45 +4136,6 @@ function printJson(value) {
|
|
|
4141
4136
|
process.stdout.write(`${JSON.stringify(value, null, 2)}\n`);
|
|
4142
4137
|
}
|
|
4143
4138
|
|
|
4144
|
-
async function ctoHygieneNotify(flags) {
|
|
4145
|
-
const rootDir = flags.root ? pathResolve(flags.root) : process.cwd();
|
|
4146
|
-
const stateFile = flags["state-file"]
|
|
4147
|
-
? pathResolve(flags["state-file"])
|
|
4148
|
-
: pathJoin(rootDir, ".triflux", "cto-hygiene-notify-state.json");
|
|
4149
|
-
|
|
4150
|
-
const projection = await runHygiene(["--dry-run", "--json"], {
|
|
4151
|
-
rootDir,
|
|
4152
|
-
dryRun: true,
|
|
4153
|
-
json: false,
|
|
4154
|
-
stdout: {
|
|
4155
|
-
write() {
|
|
4156
|
-
return true;
|
|
4157
|
-
},
|
|
4158
|
-
},
|
|
4159
|
-
});
|
|
4160
|
-
|
|
4161
|
-
const notifier = createNotifier({ stdout: process.stderr });
|
|
4162
|
-
const result = await notifyCtoHygieneOnce(projection, {
|
|
4163
|
-
notifier,
|
|
4164
|
-
stateFile,
|
|
4165
|
-
});
|
|
4166
|
-
const payload = {
|
|
4167
|
-
ok: true,
|
|
4168
|
-
stateFile,
|
|
4169
|
-
counts: projection.counts,
|
|
4170
|
-
...result,
|
|
4171
|
-
};
|
|
4172
|
-
|
|
4173
|
-
if (flags.json) {
|
|
4174
|
-
printJson(payload);
|
|
4175
|
-
return;
|
|
4176
|
-
}
|
|
4177
|
-
|
|
4178
|
-
process.stdout.write(
|
|
4179
|
-
`cto hygiene notify: ${payload.reason} (${payload.hash})\n`,
|
|
4180
|
-
);
|
|
4181
|
-
}
|
|
4182
|
-
|
|
4183
4139
|
async function main() {
|
|
4184
4140
|
const { command, flags } = parseCli(process.argv.slice(2));
|
|
4185
4141
|
|
|
@@ -4214,8 +4170,6 @@ async function main() {
|
|
|
4214
4170
|
await peer(flags);
|
|
4215
4171
|
} else if (command === "orchestrate") {
|
|
4216
4172
|
await orchestrate(flags);
|
|
4217
|
-
} else if (command === "cto-hygiene-notify") {
|
|
4218
|
-
await ctoHygieneNotify(flags);
|
|
4219
4173
|
} else {
|
|
4220
4174
|
throw new Error(`Unknown subcommand: ${command}\n${usage()}`);
|
|
4221
4175
|
}
|
|
@@ -4230,7 +4184,6 @@ export {
|
|
|
4230
4184
|
callRemoteLive,
|
|
4231
4185
|
classifyPeerExitReason,
|
|
4232
4186
|
createPeerSignalController,
|
|
4233
|
-
ctoHygieneNotify,
|
|
4234
4187
|
derivePeerStatus,
|
|
4235
4188
|
discoverClaudeTmuxSessions,
|
|
4236
4189
|
discoverCodexTmuxSessions,
|
package/bin/triflux.mjs
CHANGED
|
@@ -476,62 +476,13 @@ const CLI_COMMAND_SCHEMAS = Object.freeze({
|
|
|
476
476
|
},
|
|
477
477
|
},
|
|
478
478
|
cto: {
|
|
479
|
-
usage: "tfx cto <collect|status|
|
|
479
|
+
usage: "tfx cto <collect|status|hygiene> [options]",
|
|
480
480
|
description: "repo-local authority layer console",
|
|
481
481
|
subcommands: {
|
|
482
482
|
collect: "refresh .triflux/lake/current.json from authority sources",
|
|
483
483
|
status: "print the current authority summary",
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
steward: {
|
|
487
|
-
usage:
|
|
488
|
-
"tfx cto steward [--watch] [--max-runs N] [--interval-ms N] [--apply|--dry-run] [--json] [--no-collect]",
|
|
489
|
-
description:
|
|
490
|
-
"periodically collect and invoke one-shot CTO hygiene without destructive cleanup",
|
|
491
|
-
options: [
|
|
492
|
-
{
|
|
493
|
-
name: "--watch",
|
|
494
|
-
type: "boolean",
|
|
495
|
-
description:
|
|
496
|
-
"run repeatedly until interrupted or --max-runs is reached",
|
|
497
|
-
},
|
|
498
|
-
{
|
|
499
|
-
name: "--max-runs",
|
|
500
|
-
type: "number",
|
|
501
|
-
description:
|
|
502
|
-
"maximum steward iterations; required for --watch --json",
|
|
503
|
-
},
|
|
504
|
-
{
|
|
505
|
-
name: "--interval-ms",
|
|
506
|
-
type: "number",
|
|
507
|
-
description: "delay between watch iterations in milliseconds",
|
|
508
|
-
},
|
|
509
|
-
{
|
|
510
|
-
name: "--apply",
|
|
511
|
-
type: "boolean",
|
|
512
|
-
description:
|
|
513
|
-
"invoke hygiene apply mode (ledger acknowledgement only)",
|
|
514
|
-
},
|
|
515
|
-
{
|
|
516
|
-
name: "--dry-run",
|
|
517
|
-
type: "boolean",
|
|
518
|
-
description: "invoke hygiene dry-run mode (default)",
|
|
519
|
-
},
|
|
520
|
-
{
|
|
521
|
-
name: "--json",
|
|
522
|
-
type: "boolean",
|
|
523
|
-
description:
|
|
524
|
-
"print one finite steward JSON summary object; with --watch requires --max-runs",
|
|
525
|
-
},
|
|
526
|
-
{
|
|
527
|
-
name: "--no-collect",
|
|
528
|
-
type: "boolean",
|
|
529
|
-
description: "skip the collect pass before hygiene",
|
|
530
|
-
},
|
|
531
|
-
],
|
|
532
|
-
},
|
|
533
|
-
event:
|
|
534
|
-
"append wrapper lineage events: context-save, context-restore, pr-created, pr-merged-or-closed",
|
|
484
|
+
hygiene:
|
|
485
|
+
"project CTO hygiene counts and actionable dry-run rows (--dry-run)",
|
|
535
486
|
},
|
|
536
487
|
},
|
|
537
488
|
multi: {
|
|
@@ -2583,26 +2534,6 @@ async function cmdDoctor(options = {}) {
|
|
|
2583
2534
|
});
|
|
2584
2535
|
warn("Codex 레이트 리밋 캐시 재생성 실패");
|
|
2585
2536
|
}
|
|
2586
|
-
try {
|
|
2587
|
-
execFileSync(
|
|
2588
|
-
process.execPath,
|
|
2589
|
-
[hudScript, "--refresh-gemini-quota"],
|
|
2590
|
-
{ timeout: 15000, stdio: "ignore", windowsHide: true },
|
|
2591
|
-
);
|
|
2592
|
-
report.actions.push({
|
|
2593
|
-
type: "rebuild",
|
|
2594
|
-
name: "gemini-quota-cache",
|
|
2595
|
-
status: "ok",
|
|
2596
|
-
});
|
|
2597
|
-
ok("Gemini 쿼터 캐시 재생성됨");
|
|
2598
|
-
} catch {
|
|
2599
|
-
report.actions.push({
|
|
2600
|
-
type: "rebuild",
|
|
2601
|
-
name: "gemini-quota-cache",
|
|
2602
|
-
status: "failed",
|
|
2603
|
-
});
|
|
2604
|
-
warn("Gemini 쿼터 캐시 재생성 실패");
|
|
2605
|
-
}
|
|
2606
2537
|
}
|
|
2607
2538
|
try {
|
|
2608
2539
|
const { buildAll } = await import("../scripts/cache-warmup.mjs");
|
package/cto/collect.mjs
CHANGED
|
@@ -1,23 +1,23 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
2
|
import {
|
|
3
|
-
appendFileSync,
|
|
4
|
-
closeSync,
|
|
5
3
|
existsSync,
|
|
6
4
|
mkdirSync,
|
|
7
|
-
openSync,
|
|
8
5
|
readdirSync,
|
|
9
6
|
readFileSync,
|
|
10
|
-
renameSync,
|
|
11
7
|
statSync,
|
|
12
|
-
unlinkSync,
|
|
13
|
-
writeFileSync,
|
|
14
8
|
} from "node:fs";
|
|
15
9
|
import { homedir } from "node:os";
|
|
16
|
-
import {
|
|
10
|
+
import { join, relative } from "node:path";
|
|
17
11
|
import { fileURLToPath } from "node:url";
|
|
18
12
|
|
|
19
13
|
import { renderBrief } from "./brief.mjs";
|
|
20
|
-
import {
|
|
14
|
+
import { appendCtoEvent } from "./events.mjs";
|
|
15
|
+
import {
|
|
16
|
+
firstExisting,
|
|
17
|
+
readJsonLines,
|
|
18
|
+
resolveLakeRootDir,
|
|
19
|
+
writeAtomic,
|
|
20
|
+
} from "./lake-root.mjs";
|
|
21
21
|
|
|
22
22
|
const SCHEMA_VERSION = "cto-lake.v1";
|
|
23
23
|
const SOURCE_REGISTRY = [
|
|
@@ -69,24 +69,6 @@ const SOURCE_REGISTRY = [
|
|
|
69
69
|
probe: ".omx/handoffs/*",
|
|
70
70
|
enabled: true,
|
|
71
71
|
},
|
|
72
|
-
{
|
|
73
|
-
id: "session_vault",
|
|
74
|
-
kind: "optional-durable-ref",
|
|
75
|
-
probe: ".triflux/session-vault.json, sessions_v2.db, or .session-vault",
|
|
76
|
-
enabled: true,
|
|
77
|
-
},
|
|
78
|
-
{
|
|
79
|
-
id: "agy",
|
|
80
|
-
kind: "optional-durable-ref",
|
|
81
|
-
probe: ".agy/state.json or .agy/refs.json",
|
|
82
|
-
enabled: true,
|
|
83
|
-
},
|
|
84
|
-
{
|
|
85
|
-
id: "gbrain",
|
|
86
|
-
kind: "optional-durable-ref",
|
|
87
|
-
probe: ".gbrain/refs.json or .gbrain/config.json",
|
|
88
|
-
enabled: true,
|
|
89
|
-
},
|
|
90
72
|
];
|
|
91
73
|
const SOURCE_IDS = SOURCE_REGISTRY.map((source) => source.id);
|
|
92
74
|
|
|
@@ -104,10 +86,6 @@ function isoNow(opts) {
|
|
|
104
86
|
return value.toISOString();
|
|
105
87
|
}
|
|
106
88
|
|
|
107
|
-
function sleep(ms) {
|
|
108
|
-
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
109
|
-
}
|
|
110
|
-
|
|
111
89
|
function sourceState(available, status, detail, collectedAt) {
|
|
112
90
|
return {
|
|
113
91
|
available,
|
|
@@ -130,25 +108,6 @@ function readJson(filePath) {
|
|
|
130
108
|
return JSON.parse(readFileSync(filePath, "utf8"));
|
|
131
109
|
}
|
|
132
110
|
|
|
133
|
-
function readJsonLines(filePath, limit = 20) {
|
|
134
|
-
if (!existsSync(filePath)) return [];
|
|
135
|
-
const lines = readFileSync(filePath, "utf8")
|
|
136
|
-
.split(/\r?\n/u)
|
|
137
|
-
.map((line) => line.trim())
|
|
138
|
-
.filter(Boolean);
|
|
139
|
-
return lines.slice(-limit).flatMap((line) => {
|
|
140
|
-
try {
|
|
141
|
-
return [JSON.parse(line)];
|
|
142
|
-
} catch {
|
|
143
|
-
return [];
|
|
144
|
-
}
|
|
145
|
-
});
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function findFirstExisting(candidates) {
|
|
149
|
-
return candidates.find((candidate) => existsSync(candidate)) || null;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
111
|
function listDirFiles(dirPath, rootDir, limit = 10) {
|
|
153
112
|
if (!existsSync(dirPath)) return [];
|
|
154
113
|
try {
|
|
@@ -237,7 +196,7 @@ function collectJsonArtifact(
|
|
|
237
196
|
collectedAt,
|
|
238
197
|
summarize = safeSummary,
|
|
239
198
|
) {
|
|
240
|
-
const filePath =
|
|
199
|
+
const filePath = firstExisting(candidates);
|
|
241
200
|
if (!filePath) {
|
|
242
201
|
return missingSource(
|
|
243
202
|
"no_shell_readable_artifact",
|
|
@@ -429,59 +388,6 @@ function collectHandoffs(rootDir, collectedAt) {
|
|
|
429
388
|
);
|
|
430
389
|
}
|
|
431
390
|
|
|
432
|
-
function collectDurableRefs(
|
|
433
|
-
id,
|
|
434
|
-
candidates,
|
|
435
|
-
rootDir,
|
|
436
|
-
collectedAt,
|
|
437
|
-
unavailableStatus = "no_shell_readable_artifact",
|
|
438
|
-
) {
|
|
439
|
-
const filePath = findFirstExisting(candidates);
|
|
440
|
-
if (!filePath) {
|
|
441
|
-
return missingSource(
|
|
442
|
-
unavailableStatus,
|
|
443
|
-
`${id} exposes no shell-readable durable artifact`,
|
|
444
|
-
collectedAt,
|
|
445
|
-
);
|
|
446
|
-
}
|
|
447
|
-
try {
|
|
448
|
-
const stat = statSync(filePath);
|
|
449
|
-
if (stat.isDirectory()) {
|
|
450
|
-
return sourceState(
|
|
451
|
-
true,
|
|
452
|
-
"ok",
|
|
453
|
-
{
|
|
454
|
-
path: relPath(rootDir, filePath),
|
|
455
|
-
files: listDirFiles(filePath, rootDir, 10),
|
|
456
|
-
},
|
|
457
|
-
collectedAt,
|
|
458
|
-
);
|
|
459
|
-
}
|
|
460
|
-
const parsed = basename(filePath).endsWith(".json")
|
|
461
|
-
? readJson(filePath)
|
|
462
|
-
: readFileSync(filePath, "utf8");
|
|
463
|
-
return sourceState(
|
|
464
|
-
true,
|
|
465
|
-
"ok",
|
|
466
|
-
{
|
|
467
|
-
path: relPath(rootDir, filePath),
|
|
468
|
-
summary: safeSummary(parsed),
|
|
469
|
-
},
|
|
470
|
-
collectedAt,
|
|
471
|
-
);
|
|
472
|
-
} catch (error) {
|
|
473
|
-
return sourceState(
|
|
474
|
-
true,
|
|
475
|
-
"read_error",
|
|
476
|
-
{
|
|
477
|
-
path: relPath(rootDir, filePath),
|
|
478
|
-
error: error?.message || `${id} read failed`,
|
|
479
|
-
},
|
|
480
|
-
collectedAt,
|
|
481
|
-
);
|
|
482
|
-
}
|
|
483
|
-
}
|
|
484
|
-
|
|
485
391
|
function buildSummary(current) {
|
|
486
392
|
const repo = current.repo;
|
|
487
393
|
const omxGoals = current.sources.ultragoal_omx.detail?.active_goals || [];
|
|
@@ -633,48 +539,6 @@ function validateCurrent(current) {
|
|
|
633
539
|
}
|
|
634
540
|
}
|
|
635
541
|
|
|
636
|
-
function writeAtomic(filePath, body) {
|
|
637
|
-
mkdirSync(dirname(filePath), { recursive: true });
|
|
638
|
-
const tmpPath = join(
|
|
639
|
-
dirname(filePath),
|
|
640
|
-
`.${basename(filePath)}.${process.pid}.${Date.now()}.tmp`,
|
|
641
|
-
);
|
|
642
|
-
writeFileSync(tmpPath, body, "utf8");
|
|
643
|
-
renameSync(tmpPath, filePath);
|
|
644
|
-
}
|
|
645
|
-
|
|
646
|
-
async function appendLedgerEvent(lakeRoot, event, stderr) {
|
|
647
|
-
mkdirSync(lakeRoot, { recursive: true });
|
|
648
|
-
const ledgerPath = join(lakeRoot, "ledger.jsonl");
|
|
649
|
-
const lockPath = join(lakeRoot, "ledger.jsonl.lock");
|
|
650
|
-
let fd = null;
|
|
651
|
-
for (let attempt = 0; attempt < 3; attempt += 1) {
|
|
652
|
-
try {
|
|
653
|
-
fd = openSync(lockPath, "wx", 0o600);
|
|
654
|
-
break;
|
|
655
|
-
} catch (error) {
|
|
656
|
-
if (error?.code !== "EEXIST") throw error;
|
|
657
|
-
if (attempt < 2) await sleep(100);
|
|
658
|
-
}
|
|
659
|
-
}
|
|
660
|
-
if (fd === null) {
|
|
661
|
-
stderr.write(
|
|
662
|
-
"[tfx cto collect] warning: ledger lock timeout; skipped ledger append\n",
|
|
663
|
-
);
|
|
664
|
-
return false;
|
|
665
|
-
}
|
|
666
|
-
|
|
667
|
-
try {
|
|
668
|
-
appendFileSync(ledgerPath, `${JSON.stringify(event)}\n`, "utf8");
|
|
669
|
-
return true;
|
|
670
|
-
} finally {
|
|
671
|
-
closeSync(fd);
|
|
672
|
-
try {
|
|
673
|
-
unlinkSync(lockPath);
|
|
674
|
-
} catch {}
|
|
675
|
-
}
|
|
676
|
-
}
|
|
677
|
-
|
|
678
542
|
function collectSources(rootDir, collectedAt, execFileSyncFn, opts = {}) {
|
|
679
543
|
const home = homedir();
|
|
680
544
|
const hostArtifactCandidates =
|
|
@@ -748,34 +612,6 @@ function collectSources(rootDir, collectedAt, execFileSyncFn, opts = {}) {
|
|
|
748
612
|
collectedAt,
|
|
749
613
|
),
|
|
750
614
|
handoffs: collectHandoffs(rootDir, collectedAt),
|
|
751
|
-
session_vault: collectDurableRefs(
|
|
752
|
-
"session_vault",
|
|
753
|
-
[
|
|
754
|
-
join(rootDir, ".triflux", "session-vault.json"),
|
|
755
|
-
join(rootDir, "sessions_v2.db"),
|
|
756
|
-
join(rootDir, ".session-vault"),
|
|
757
|
-
],
|
|
758
|
-
rootDir,
|
|
759
|
-
collectedAt,
|
|
760
|
-
"not_shell_collectable",
|
|
761
|
-
),
|
|
762
|
-
agy: collectDurableRefs(
|
|
763
|
-
"agy",
|
|
764
|
-
[join(rootDir, ".agy", "state.json"), join(rootDir, ".agy", "refs.json")],
|
|
765
|
-
rootDir,
|
|
766
|
-
collectedAt,
|
|
767
|
-
"not_shell_collectable",
|
|
768
|
-
),
|
|
769
|
-
gbrain: collectDurableRefs(
|
|
770
|
-
"gbrain",
|
|
771
|
-
[
|
|
772
|
-
join(rootDir, ".gbrain", "refs.json"),
|
|
773
|
-
join(rootDir, ".gbrain", "config.json"),
|
|
774
|
-
],
|
|
775
|
-
rootDir,
|
|
776
|
-
collectedAt,
|
|
777
|
-
"not_shell_collectable",
|
|
778
|
-
),
|
|
779
615
|
};
|
|
780
616
|
|
|
781
617
|
return { repo: git.repo, sources };
|
|
@@ -786,6 +622,12 @@ function hasFlag(args, flag) {
|
|
|
786
622
|
}
|
|
787
623
|
|
|
788
624
|
export async function runCollect(args = [], opts = {}) {
|
|
625
|
+
if (hasFlag(args, "--help")) {
|
|
626
|
+
(opts.stdout || process.stdout).write(
|
|
627
|
+
"Usage: tfx cto collect [--json]\nRefresh the CTO lake from durable authority sources.\n",
|
|
628
|
+
);
|
|
629
|
+
return;
|
|
630
|
+
}
|
|
789
631
|
const rootDir = opts.rootDir || resolveLakeRootDir(process.cwd());
|
|
790
632
|
const lakeRoot = opts.lakeRoot || join(rootDir, ".triflux", "lake");
|
|
791
633
|
const stdout = opts.stdout || process.stdout;
|
|
@@ -822,7 +664,7 @@ export async function runCollect(args = [], opts = {}) {
|
|
|
822
664
|
sources_json: "sources.json",
|
|
823
665
|
},
|
|
824
666
|
};
|
|
825
|
-
const appended = await
|
|
667
|
+
const { appended } = await appendCtoEvent(lakeRoot, event, { stderr });
|
|
826
668
|
if (appended) current.ledger_tail = readLedgerTail(lakeRoot);
|
|
827
669
|
|
|
828
670
|
validateCurrent(current);
|
package/cto/current.schema.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
3
3
|
"$id": "https://triflux.local/schemas/cto/current.schema.json",
|
|
4
4
|
"title": "Triflux CTO Lake Current Authority State",
|
|
5
|
-
"description": "Contract for .triflux/lake/current.json. .triflux/lake is a triflux-owned, namespace-neutral cross-agent rollup for durable artifacts across Claude, Codex/OMX, OMC,
|
|
5
|
+
"description": "Contract for .triflux/lake/current.json. .triflux/lake is a triflux-owned, namespace-neutral cross-agent rollup for durable artifacts across Claude, Codex/OMX, OMC, and Triflux state. It reads and aggregates .omx/ultragoal and .omc/ultragoal execution ledgers without replacing or duplicating their authority. ledger.jsonl and sources.json companion contracts are documented in $defs.",
|
|
6
6
|
"type": "object",
|
|
7
7
|
"additionalProperties": false,
|
|
8
8
|
"required": [
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
},
|
|
45
45
|
"sources": {
|
|
46
46
|
"type": "object",
|
|
47
|
-
"description": "Durable artifact source states only.
|
|
47
|
+
"description": "Durable artifact source states only. A source reports available:false when no shell-readable durable artifact exists.",
|
|
48
48
|
"additionalProperties": false,
|
|
49
49
|
"required": [
|
|
50
50
|
"git",
|
|
@@ -54,10 +54,7 @@
|
|
|
54
54
|
"tfx_synapse",
|
|
55
55
|
"ultragoal_omx",
|
|
56
56
|
"ultragoal_omc",
|
|
57
|
-
"handoffs"
|
|
58
|
-
"session_vault",
|
|
59
|
-
"agy",
|
|
60
|
-
"gbrain"
|
|
57
|
+
"handoffs"
|
|
61
58
|
],
|
|
62
59
|
"properties": {
|
|
63
60
|
"git": {
|
|
@@ -83,21 +80,12 @@
|
|
|
83
80
|
},
|
|
84
81
|
"handoffs": {
|
|
85
82
|
"$ref": "#/$defs/sourceState"
|
|
86
|
-
},
|
|
87
|
-
"session_vault": {
|
|
88
|
-
"$ref": "#/$defs/sourceState"
|
|
89
|
-
},
|
|
90
|
-
"agy": {
|
|
91
|
-
"$ref": "#/$defs/sourceState"
|
|
92
|
-
},
|
|
93
|
-
"gbrain": {
|
|
94
|
-
"$ref": "#/$defs/sourceState"
|
|
95
83
|
}
|
|
96
84
|
}
|
|
97
85
|
},
|
|
98
86
|
"summary": {
|
|
99
87
|
"type": "object",
|
|
100
|
-
"description": "Human-
|
|
88
|
+
"description": "Human-oriented synthesized authority summary derived from durable artifacts and live Triflux synapse session status. Phase 1 may add source-derived fields here without changing the top-level contract.",
|
|
101
89
|
"additionalProperties": true
|
|
102
90
|
},
|
|
103
91
|
"ledger_tail": {
|