@akiojin/gwt 6.30.3 → 9.0.1
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/.cargo/config.toml +2 -0
- package/.claude-plugin/marketplace.json +18 -0
- package/.coderabbit.yaml +8 -0
- package/.codex/skills/gwt-fix-issue/scripts/inspect_issue.py +833 -0
- package/.dockerignore +63 -0
- package/.gitattributes +27 -0
- package/.husky/commit-msg +2 -0
- package/.husky/pre-commit +9 -0
- package/.husky/pre-push +12 -0
- package/.markdownlint.json +18 -0
- package/.markdownlintignore +2 -0
- package/Dockerfile +58 -0
- package/README.ja.md +161 -484
- package/README.md +164 -444
- package/cliff.toml +56 -0
- package/clippy.toml +2 -0
- package/cmake/ci-disable-native.cmake +16 -0
- package/codecov.yml +16 -0
- package/commitlint.config.cjs +107 -0
- package/deny.toml +35 -0
- package/docker-compose.yml +59 -0
- package/messages/errors.toml +52 -0
- package/package.json +12 -22
- package/rustfmt.toml +8 -0
- package/scripts/check-e2e-coverage-threshold.mjs +238 -0
- package/scripts/entrypoint.sh +36 -25
- package/scripts/install-linux-deps.sh +46 -0
- package/scripts/postinstall.js +79 -227
- package/scripts/release_issue_refs.py +317 -0
- package/scripts/run-local-backend-tests-on-commit.sh +15 -0
- package/scripts/run-local-e2e-coverage-on-commit.sh +69 -0
- package/scripts/run-local-e2e-on-commit.sh +60 -0
- package/scripts/test-all.sh +13 -0
- package/scripts/test_release_issue_refs.py +257 -0
- package/scripts/validate-skill-frontmatter.sh +108 -0
- package/scripts/verify-ci-node-toolchain.sh +76 -0
- package/scripts/verify-husky-hooks.sh +6 -0
- package/scripts/voice-eval.sh +48 -0
- package/tests/voice_eval/README.md +53 -0
- package/tests/voice_eval/manifest.template.json +55 -0
- package/tests/voice_eval/samples/.gitkeep +1 -0
- package/tests/voice_eval/script-ja.txt +10 -0
- package/vendor/ratatui-core/src/backend/test.rs +1077 -0
- package/vendor/ratatui-core/src/backend.rs +405 -0
- package/vendor/ratatui-core/src/buffer/assert.rs +71 -0
- package/vendor/ratatui-core/src/buffer/buffer.rs +1388 -0
- package/vendor/ratatui-core/src/buffer/cell.rs +377 -0
- package/vendor/ratatui-core/src/buffer.rs +9 -0
- package/vendor/ratatui-core/src/layout/alignment.rs +89 -0
- package/vendor/ratatui-core/src/layout/constraint.rs +526 -0
- package/vendor/ratatui-core/src/layout/direction.rs +63 -0
- package/vendor/ratatui-core/src/layout/flex.rs +212 -0
- package/vendor/ratatui-core/src/layout/layout.rs +2838 -0
- package/vendor/ratatui-core/src/layout/margin.rs +79 -0
- package/vendor/ratatui-core/src/layout/offset.rs +66 -0
- package/vendor/ratatui-core/src/layout/position.rs +253 -0
- package/vendor/ratatui-core/src/layout/rect/iter.rs +356 -0
- package/vendor/ratatui-core/src/layout/rect/ops.rs +136 -0
- package/vendor/ratatui-core/src/layout/rect.rs +1114 -0
- package/vendor/ratatui-core/src/layout/size.rs +147 -0
- package/vendor/ratatui-core/src/layout.rs +333 -0
- package/vendor/ratatui-core/src/lib.rs +82 -0
- package/vendor/ratatui-core/src/style/anstyle.rs +348 -0
- package/vendor/ratatui-core/src/style/color.rs +788 -0
- package/vendor/ratatui-core/src/style/palette/material.rs +608 -0
- package/vendor/ratatui-core/src/style/palette/tailwind.rs +653 -0
- package/vendor/ratatui-core/src/style/palette.rs +6 -0
- package/vendor/ratatui-core/src/style/palette_conversion.rs +82 -0
- package/vendor/ratatui-core/src/style/stylize.rs +668 -0
- package/vendor/ratatui-core/src/style.rs +1069 -0
- package/vendor/ratatui-core/src/symbols/bar.rs +51 -0
- package/vendor/ratatui-core/src/symbols/block.rs +51 -0
- package/vendor/ratatui-core/src/symbols/border.rs +709 -0
- package/vendor/ratatui-core/src/symbols/braille.rs +21 -0
- package/vendor/ratatui-core/src/symbols/half_block.rs +3 -0
- package/vendor/ratatui-core/src/symbols/line.rs +259 -0
- package/vendor/ratatui-core/src/symbols/marker.rs +82 -0
- package/vendor/ratatui-core/src/symbols/merge.rs +748 -0
- package/vendor/ratatui-core/src/symbols/pixel.rs +30 -0
- package/vendor/ratatui-core/src/symbols/scrollbar.rs +46 -0
- package/vendor/ratatui-core/src/symbols/shade.rs +5 -0
- package/vendor/ratatui-core/src/symbols.rs +15 -0
- package/vendor/ratatui-core/src/terminal/frame.rs +192 -0
- package/vendor/ratatui-core/src/terminal/terminal.rs +926 -0
- package/vendor/ratatui-core/src/terminal/viewport.rs +58 -0
- package/vendor/ratatui-core/src/terminal.rs +40 -0
- package/vendor/ratatui-core/src/text/grapheme.rs +84 -0
- package/vendor/ratatui-core/src/text/line.rs +1678 -0
- package/vendor/ratatui-core/src/text/masked.rs +149 -0
- package/vendor/ratatui-core/src/text/span.rs +904 -0
- package/vendor/ratatui-core/src/text/text.rs +1434 -0
- package/vendor/ratatui-core/src/text.rs +64 -0
- package/vendor/ratatui-core/src/widgets/stateful_widget.rs +193 -0
- package/vendor/ratatui-core/src/widgets/widget.rs +174 -0
- package/vendor/ratatui-core/src/widgets.rs +9 -0
- package/bin/gwt.js +0 -131
- package/scripts/postinstall.test.js +0 -71
- package/scripts/release-download.js +0 -66
package/cliff.toml
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
# git-cliff configuration
|
|
2
|
+
# https://git-cliff.org/docs/configuration
|
|
3
|
+
|
|
4
|
+
[changelog]
|
|
5
|
+
header = """
|
|
6
|
+
# Changelog
|
|
7
|
+
|
|
8
|
+
All notable changes to this project will be documented in this file.
|
|
9
|
+
"""
|
|
10
|
+
body = """
|
|
11
|
+
{% if version %}\
|
|
12
|
+
## [{{ version | trim_start_matches(pat="v") }}] - {{ timestamp | date(format="%Y-%m-%d") }}
|
|
13
|
+
{% else %}\
|
|
14
|
+
## [Unreleased]
|
|
15
|
+
{% endif %}\
|
|
16
|
+
{% for group, commits in commits | group_by(attribute="group") %}
|
|
17
|
+
### {{ group | striptags | trim | upper_first }}
|
|
18
|
+
{% for commit in commits %}
|
|
19
|
+
- {% if commit.scope %}**{{ commit.scope }}:** {% endif %}\
|
|
20
|
+
{{ commit.message | upper_first }}\
|
|
21
|
+
{% if commit.remote.username %} by @{{ commit.remote.username }}{%- endif %}\
|
|
22
|
+
{% endfor %}
|
|
23
|
+
{% endfor %}\n
|
|
24
|
+
"""
|
|
25
|
+
footer = ""
|
|
26
|
+
trim = true
|
|
27
|
+
|
|
28
|
+
[git]
|
|
29
|
+
conventional_commits = true
|
|
30
|
+
filter_unconventional = true
|
|
31
|
+
split_commits = false
|
|
32
|
+
commit_parsers = [
|
|
33
|
+
{ message = "^feat", group = "Features" },
|
|
34
|
+
{ message = "^fix", group = "Bug Fixes" },
|
|
35
|
+
{ message = "^doc", group = "Documentation" },
|
|
36
|
+
{ message = "^perf", group = "Performance" },
|
|
37
|
+
{ message = "^refactor", group = "Refactor" },
|
|
38
|
+
{ message = "^style", group = "Styling" },
|
|
39
|
+
{ message = "^test", group = "Testing" },
|
|
40
|
+
{ message = "^chore\\(release\\)", skip = true },
|
|
41
|
+
{ message = "^chore\\(deps\\)", skip = true },
|
|
42
|
+
{ message = "^chore", group = "Miscellaneous Tasks" },
|
|
43
|
+
{ body = ".*security", group = "Security" },
|
|
44
|
+
]
|
|
45
|
+
protect_breaking_commits = false
|
|
46
|
+
filter_commits = false
|
|
47
|
+
topo_order = false
|
|
48
|
+
sort_commits = "oldest"
|
|
49
|
+
tag_pattern = "v[0-9].*"
|
|
50
|
+
|
|
51
|
+
[bump]
|
|
52
|
+
# chore/docsのみでもpatchバージョンを上げる
|
|
53
|
+
features_always_bump_minor = true
|
|
54
|
+
breaking_always_bump_major = true
|
|
55
|
+
# デフォルトでpatch bump(feat/fix/breakingがない場合)
|
|
56
|
+
initial_tag = "v0.0.0"
|
package/clippy.toml
ADDED
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# CI toolchain override for macOS GitHub Actions runners.
|
|
2
|
+
#
|
|
3
|
+
# whisper-rs-sys passes CMAKE_* env vars to cmake, so we use
|
|
4
|
+
# CMAKE_TOOLCHAIN_FILE pointing here. Toolchain-file cache variables
|
|
5
|
+
# are applied before project() and take precedence over defaults.
|
|
6
|
+
#
|
|
7
|
+
# 1. Disable GGML_NATIVE to avoid ARM i8mm intrinsic errors
|
|
8
|
+
# (Xcode 16.4 Apple Clang + -mcpu=native).
|
|
9
|
+
set(GGML_NATIVE OFF CACHE BOOL "Disable native CPU optimizations for CI" FORCE)
|
|
10
|
+
#
|
|
11
|
+
# 2. Set deployment target to macOS 11.0+.
|
|
12
|
+
# ggml uses std::filesystem (requires 10.15+); ARM Macs need 11.0+.
|
|
13
|
+
# cmake-rs (via cc crate) may inject -mmacosx-version-min=10.13 into
|
|
14
|
+
# CMAKE_C_FLAGS, but cmake appends its own flag AFTER those, and
|
|
15
|
+
# clang uses the last -mmacosx-version-min it sees.
|
|
16
|
+
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "macOS 11.0+ for ARM and std::filesystem" FORCE)
|
package/codecov.yml
ADDED
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
const resolveLocal = (specifier) => {
|
|
2
|
+
try {
|
|
3
|
+
return require.resolve(specifier, { paths: [__dirname] });
|
|
4
|
+
} catch {
|
|
5
|
+
return null;
|
|
6
|
+
}
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const hasConventionalConfig = Boolean(
|
|
10
|
+
resolveLocal("@commitlint/config-conventional"),
|
|
11
|
+
);
|
|
12
|
+
const hasConventionalParser = Boolean(
|
|
13
|
+
resolveLocal("conventional-changelog-conventionalcommits"),
|
|
14
|
+
);
|
|
15
|
+
|
|
16
|
+
const severity = {
|
|
17
|
+
off: 0,
|
|
18
|
+
warn: 1,
|
|
19
|
+
error: 2,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
const conventionalTypeEnum = [
|
|
23
|
+
"build",
|
|
24
|
+
"chore",
|
|
25
|
+
"ci",
|
|
26
|
+
"docs",
|
|
27
|
+
"feat",
|
|
28
|
+
"fix",
|
|
29
|
+
"perf",
|
|
30
|
+
"refactor",
|
|
31
|
+
"revert",
|
|
32
|
+
"style",
|
|
33
|
+
"test",
|
|
34
|
+
];
|
|
35
|
+
|
|
36
|
+
const customTypeEnum = [
|
|
37
|
+
"feat",
|
|
38
|
+
"fix",
|
|
39
|
+
"docs",
|
|
40
|
+
"style",
|
|
41
|
+
"refactor",
|
|
42
|
+
"perf",
|
|
43
|
+
"test",
|
|
44
|
+
"build",
|
|
45
|
+
"ci",
|
|
46
|
+
"chore",
|
|
47
|
+
"revert",
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
const conventionalRules = {
|
|
51
|
+
"body-leading-blank": [severity.warn, "always"],
|
|
52
|
+
"body-max-line-length": [severity.error, "always", 100],
|
|
53
|
+
"footer-leading-blank": [severity.warn, "always"],
|
|
54
|
+
"footer-max-line-length": [severity.error, "always", 100],
|
|
55
|
+
"header-max-length": [severity.error, "always", 100],
|
|
56
|
+
"header-trim": [severity.error, "always"],
|
|
57
|
+
"subject-case": [
|
|
58
|
+
severity.error,
|
|
59
|
+
"never",
|
|
60
|
+
["sentence-case", "start-case", "pascal-case", "upper-case"],
|
|
61
|
+
],
|
|
62
|
+
"subject-empty": [severity.error, "never"],
|
|
63
|
+
"subject-full-stop": [severity.error, "never", "."],
|
|
64
|
+
"type-case": [severity.error, "always", "lower-case"],
|
|
65
|
+
"type-empty": [severity.error, "never"],
|
|
66
|
+
"type-enum": [severity.error, "always", conventionalTypeEnum],
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
const customRules = {
|
|
70
|
+
"subject-empty": [2, "never"],
|
|
71
|
+
"subject-max-length": [2, "always", 100],
|
|
72
|
+
"header-max-length": [2, "always", 100],
|
|
73
|
+
"subject-case": [0],
|
|
74
|
+
"body-max-line-length": [2, "always", 100],
|
|
75
|
+
"type-enum": [2, "always", customTypeEnum],
|
|
76
|
+
};
|
|
77
|
+
|
|
78
|
+
const rules = hasConventionalConfig
|
|
79
|
+
? customRules
|
|
80
|
+
: { ...conventionalRules, ...customRules };
|
|
81
|
+
|
|
82
|
+
module.exports = {
|
|
83
|
+
...(hasConventionalConfig
|
|
84
|
+
? { extends: ["@commitlint/config-conventional"] }
|
|
85
|
+
: hasConventionalParser
|
|
86
|
+
? { parserPreset: "conventional-changelog-conventionalcommits" }
|
|
87
|
+
: {}),
|
|
88
|
+
rules,
|
|
89
|
+
// Ignore commits that don't follow Conventional Commits format
|
|
90
|
+
ignores: [
|
|
91
|
+
(commit) => {
|
|
92
|
+
const firstLine = commit.split("\n")[0].trim();
|
|
93
|
+
// Merge commits (git-generated)
|
|
94
|
+
if (/^merge(\s|:)/i.test(firstLine)) return true;
|
|
95
|
+
// Branch-name-style commits (historical)
|
|
96
|
+
if (/^(bugfix|feature|hotfix|release)\//.test(firstLine)) return true;
|
|
97
|
+
// Historical commits without conventional prefix (Fix/Stabilize pattern)
|
|
98
|
+
if (
|
|
99
|
+
/^(Fix|Stabilize|Update|Add|Remove|Refactor|Clean|Format|Resolve)\s/.test(
|
|
100
|
+
firstLine,
|
|
101
|
+
)
|
|
102
|
+
)
|
|
103
|
+
return true;
|
|
104
|
+
return false;
|
|
105
|
+
},
|
|
106
|
+
],
|
|
107
|
+
};
|
package/deny.toml
ADDED
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
# cargo-deny configuration
|
|
2
|
+
# https://embarkstudios.github.io/cargo-deny/
|
|
3
|
+
|
|
4
|
+
[advisories]
|
|
5
|
+
db-path = "~/.cargo/advisory-db"
|
|
6
|
+
db-urls = ["https://github.com/rustsec/advisory-db"]
|
|
7
|
+
vulnerability = "deny"
|
|
8
|
+
unmaintained = "warn"
|
|
9
|
+
yanked = "warn"
|
|
10
|
+
notice = "warn"
|
|
11
|
+
|
|
12
|
+
[licenses]
|
|
13
|
+
unlicensed = "deny"
|
|
14
|
+
allow = [
|
|
15
|
+
"MIT",
|
|
16
|
+
"Apache-2.0",
|
|
17
|
+
"Apache-2.0 WITH LLVM-exception",
|
|
18
|
+
"BSD-2-Clause",
|
|
19
|
+
"BSD-3-Clause",
|
|
20
|
+
"ISC",
|
|
21
|
+
"Zlib",
|
|
22
|
+
"CC0-1.0",
|
|
23
|
+
"Unicode-DFS-2016",
|
|
24
|
+
]
|
|
25
|
+
copyleft = "deny"
|
|
26
|
+
|
|
27
|
+
[bans]
|
|
28
|
+
multiple-versions = "warn"
|
|
29
|
+
wildcards = "deny"
|
|
30
|
+
highlight = "all"
|
|
31
|
+
|
|
32
|
+
[sources]
|
|
33
|
+
unknown-registry = "deny"
|
|
34
|
+
unknown-git = "deny"
|
|
35
|
+
allow-registry = ["https://github.com/rust-lang/crates.io-index"]
|
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
services:
|
|
2
|
+
gwt:
|
|
3
|
+
build:
|
|
4
|
+
context: .
|
|
5
|
+
dockerfile: Dockerfile
|
|
6
|
+
volumes:
|
|
7
|
+
- .:/gwt
|
|
8
|
+
- bash-history:/root/.bash_history_dir
|
|
9
|
+
- claude-config:/root/.claude
|
|
10
|
+
- codex-config:/root/.codex
|
|
11
|
+
- ${HOME:?HOME must be set}/.claude:/root/.claude-host:ro
|
|
12
|
+
- ${HOME:?HOME must be set}/.codex:/root/.codex-host:ro
|
|
13
|
+
ports:
|
|
14
|
+
- ${PORT:-3000}:3000
|
|
15
|
+
extra_hosts:
|
|
16
|
+
- "host.docker.internal:host-gateway"
|
|
17
|
+
environment:
|
|
18
|
+
- NPM_USER
|
|
19
|
+
- NPM_EMAIL
|
|
20
|
+
- NPM_PASS
|
|
21
|
+
- GIT_USER_EMAIL
|
|
22
|
+
- GIT_USER_NAME
|
|
23
|
+
- GITHUB_TOKEN
|
|
24
|
+
- GITHUB_PERSONAL_ACCESS_TOKEN
|
|
25
|
+
- GITHUB_USERNAME
|
|
26
|
+
- HISTFILE=/root/.bash_history_dir/.bash_history
|
|
27
|
+
- HISTSIZE=100000
|
|
28
|
+
- HISTFILESIZE=100000
|
|
29
|
+
- MAX_MCP_OUTPUT_TOKENS
|
|
30
|
+
- ENABLE_BACKGROUND_TASKS=1
|
|
31
|
+
- FORCE_AUTO_BACKGROUND_TASKS=1
|
|
32
|
+
- PORT=3000
|
|
33
|
+
stdin_open: true
|
|
34
|
+
tty: true
|
|
35
|
+
working_dir: /gwt
|
|
36
|
+
|
|
37
|
+
playwright-novnc:
|
|
38
|
+
image: ghcr.io/xtr-dev/mcp-playwright-novnc:latest
|
|
39
|
+
platform: ${PLAYWRIGHT_NOVNC_PLATFORM:-linux/amd64}
|
|
40
|
+
ports:
|
|
41
|
+
- ${PLAYWRIGHT_NOVNC_PORT:-6080}:6080
|
|
42
|
+
- ${PLAYWRIGHT_NOVNC_VNC_PORT:-3080}:3080
|
|
43
|
+
volumes:
|
|
44
|
+
- .:/gwt
|
|
45
|
+
environment:
|
|
46
|
+
- SCREEN_WIDTH=1920
|
|
47
|
+
- SCREEN_HEIGHT=1080
|
|
48
|
+
- SCREEN_DEPTH=24
|
|
49
|
+
- MCP_BROWSER=chromium
|
|
50
|
+
- PLAYWRIGHT_BASE_URL=http://gwt:3000
|
|
51
|
+
working_dir: /gwt
|
|
52
|
+
shm_size: 2gb
|
|
53
|
+
depends_on:
|
|
54
|
+
- gwt
|
|
55
|
+
|
|
56
|
+
volumes:
|
|
57
|
+
bash-history: {}
|
|
58
|
+
claude-config: {}
|
|
59
|
+
codex-config: {}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
[git]
|
|
2
|
+
E1001 = "Repository not found"
|
|
3
|
+
E1002 = "Not a git repository"
|
|
4
|
+
E1003 = "Branch not found"
|
|
5
|
+
E1004 = "Branch already exists"
|
|
6
|
+
E1005 = "Remote not found"
|
|
7
|
+
E1006 = "Fetch failed"
|
|
8
|
+
E1007 = "Fast-forward pull failed"
|
|
9
|
+
E1008 = "Uncommitted changes detected"
|
|
10
|
+
E1009 = "Unpushed commits detected"
|
|
11
|
+
E1010 = "Branch diverged from remote"
|
|
12
|
+
E1011 = "Git command failed"
|
|
13
|
+
E1012 = "Git executable not found"
|
|
14
|
+
E1013 = "Git operation failed"
|
|
15
|
+
E1014 = "Branch create failed"
|
|
16
|
+
E1015 = "Branch delete failed"
|
|
17
|
+
|
|
18
|
+
[worktree]
|
|
19
|
+
E2001 = "Worktree not found"
|
|
20
|
+
E2002 = "Worktree already exists"
|
|
21
|
+
E2003 = "Failed to create worktree"
|
|
22
|
+
E2004 = "Failed to remove worktree"
|
|
23
|
+
E2005 = "Protected branch cannot be deleted"
|
|
24
|
+
E2006 = "Worktree path invalid"
|
|
25
|
+
E2007 = "Orphaned worktree detected"
|
|
26
|
+
E2008 = "Worktree locked by another process"
|
|
27
|
+
E2009 = "Path exists but is not a stale worktree"
|
|
28
|
+
|
|
29
|
+
[config]
|
|
30
|
+
E3001 = "Configuration file not found"
|
|
31
|
+
E3002 = "Configuration parse error"
|
|
32
|
+
E3003 = "Configuration write error"
|
|
33
|
+
E3004 = "Invalid configuration value"
|
|
34
|
+
E3005 = "Profile not found"
|
|
35
|
+
E3006 = "Session not found"
|
|
36
|
+
E3007 = "Configuration migration failed"
|
|
37
|
+
|
|
38
|
+
[agent]
|
|
39
|
+
E4001 = "Agent not found"
|
|
40
|
+
E4002 = "Agent launch failed"
|
|
41
|
+
E4003 = "Agent configuration invalid"
|
|
42
|
+
E4004 = "Agent process terminated unexpectedly"
|
|
43
|
+
|
|
44
|
+
[web]
|
|
45
|
+
E5001 = "Server bind failed"
|
|
46
|
+
E5002 = "WebSocket connection failed"
|
|
47
|
+
E5003 = "API request failed"
|
|
48
|
+
E5004 = "PTY spawn failed"
|
|
49
|
+
|
|
50
|
+
[general]
|
|
51
|
+
E9001 = "IO error"
|
|
52
|
+
E9002 = "Internal error"
|
package/package.json
CHANGED
|
@@ -1,14 +1,19 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@akiojin/gwt",
|
|
3
|
-
"version": "
|
|
4
|
-
"description": "
|
|
3
|
+
"version": "9.0.1",
|
|
4
|
+
"description": "TUI for Git worktree management and coding agent launch",
|
|
5
|
+
"type": "module",
|
|
5
6
|
"bin": {
|
|
6
|
-
"gwt": "bin/gwt
|
|
7
|
+
"gwt": "bin/gwt-tui"
|
|
7
8
|
},
|
|
8
|
-
"type": "module",
|
|
9
9
|
"scripts": {
|
|
10
10
|
"postinstall": "node scripts/postinstall.js",
|
|
11
|
+
"dev": "cargo run -p gwt-tui",
|
|
12
|
+
"build": "cargo build --release -p gwt-tui",
|
|
13
|
+
"test": "cargo test -p gwt-core -p gwt-tui --all-features",
|
|
14
|
+
"test:all": "bash scripts/test-all.sh",
|
|
11
15
|
"prepare": "test -n \"$CI\" || bunx husky install",
|
|
16
|
+
"lint:skills": "bash scripts/validate-skill-frontmatter.sh",
|
|
12
17
|
"lint:husky": "bash scripts/verify-husky-hooks.sh"
|
|
13
18
|
},
|
|
14
19
|
"keywords": [
|
|
@@ -30,27 +35,12 @@
|
|
|
30
35
|
"type": "git",
|
|
31
36
|
"url": "https://github.com/akiojin/gwt.git"
|
|
32
37
|
},
|
|
33
|
-
"publishConfig": {
|
|
34
|
-
"access": "public"
|
|
35
|
-
},
|
|
36
|
-
"files": [
|
|
37
|
-
"bin/",
|
|
38
|
-
"scripts/"
|
|
39
|
-
],
|
|
40
38
|
"engines": {
|
|
41
39
|
"node": ">=18"
|
|
42
40
|
},
|
|
43
|
-
"
|
|
44
|
-
"darwin",
|
|
45
|
-
"linux",
|
|
46
|
-
"win32"
|
|
47
|
-
],
|
|
48
|
-
"cpu": [
|
|
49
|
-
"x64",
|
|
50
|
-
"arm64"
|
|
51
|
-
],
|
|
41
|
+
"packageManager": "pnpm@10.29.2",
|
|
52
42
|
"devDependencies": {
|
|
53
|
-
"@commitlint/cli": "^20.
|
|
54
|
-
"@commitlint/config-conventional": "^20.
|
|
43
|
+
"@commitlint/cli": "^20.5.0",
|
|
44
|
+
"@commitlint/config-conventional": "^20.5.0"
|
|
55
45
|
}
|
|
56
46
|
}
|
package/rustfmt.toml
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import fs from "node:fs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
|
|
7
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const repoRoot = path.resolve(scriptDir, "..");
|
|
9
|
+
const guiDir = path.join(repoRoot, "gwt-gui");
|
|
10
|
+
const nycrcPath = path.join(guiDir, ".nycrc.e2e.json");
|
|
11
|
+
const nycTempDir = path.join(guiDir, ".nyc_output");
|
|
12
|
+
|
|
13
|
+
function loadJson(filePath) {
|
|
14
|
+
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function clone(value) {
|
|
18
|
+
return JSON.parse(JSON.stringify(value));
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function mergeCounts(existing, incoming) {
|
|
22
|
+
for (const [id, count] of Object.entries(incoming)) {
|
|
23
|
+
existing[id] = (existing[id] ?? 0) + Number(count ?? 0);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function mergeBranchCounts(existing, incoming) {
|
|
28
|
+
for (const [id, counts] of Object.entries(incoming)) {
|
|
29
|
+
const nextCounts = Array.isArray(counts) ? counts : [];
|
|
30
|
+
const mergedCounts = Array.isArray(existing[id]) ? existing[id] : [];
|
|
31
|
+
existing[id] = nextCounts.map(
|
|
32
|
+
(count, index) => Number(count ?? 0) + Number(mergedCounts[index] ?? 0),
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function mergeCoverageMaps(jsonFiles) {
|
|
38
|
+
const merged = new Map();
|
|
39
|
+
|
|
40
|
+
for (const jsonFile of jsonFiles) {
|
|
41
|
+
const coverageMap = loadJson(path.join(nycTempDir, jsonFile));
|
|
42
|
+
for (const [sourcePath, fileCoverage] of Object.entries(coverageMap)) {
|
|
43
|
+
if (!merged.has(sourcePath)) {
|
|
44
|
+
merged.set(sourcePath, clone(fileCoverage));
|
|
45
|
+
continue;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const target = merged.get(sourcePath);
|
|
49
|
+
mergeCounts(target.s, fileCoverage.s ?? {});
|
|
50
|
+
mergeCounts(target.f, fileCoverage.f ?? {});
|
|
51
|
+
mergeBranchCounts(target.b, fileCoverage.b ?? {});
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return merged;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function countCovered(counter) {
|
|
59
|
+
return Object.values(counter).filter((value) => Number(value) > 0).length;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function getSourceLineCount(filePath) {
|
|
63
|
+
try {
|
|
64
|
+
return fs.readFileSync(filePath, "utf8").split("\n").length;
|
|
65
|
+
} catch {
|
|
66
|
+
return Number.POSITIVE_INFINITY;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function isThinWrapperFile(filePath) {
|
|
71
|
+
return getSourceLineCount(filePath) <= 10;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function summarizeLines(statementMap, statementCounts) {
|
|
75
|
+
const lineHits = new Map();
|
|
76
|
+
for (const [id, location] of Object.entries(statementMap ?? {})) {
|
|
77
|
+
const count = Number(statementCounts?.[id] ?? 0);
|
|
78
|
+
const startLine = Number(location?.start?.line ?? 0);
|
|
79
|
+
const endLine = Number(location?.end?.line ?? startLine);
|
|
80
|
+
for (let line = startLine; line <= endLine; line += 1) {
|
|
81
|
+
if (!lineHits.has(line)) {
|
|
82
|
+
lineHits.set(line, false);
|
|
83
|
+
}
|
|
84
|
+
if (count > 0) {
|
|
85
|
+
lineHits.set(line, true);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
return {
|
|
91
|
+
covered: [...lineHits.values()].filter(Boolean).length,
|
|
92
|
+
total: lineHits.size,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function summarizeFile(fileCoverage, filePath) {
|
|
97
|
+
const statements = {
|
|
98
|
+
covered: countCovered(fileCoverage.s ?? {}),
|
|
99
|
+
total: Object.keys(fileCoverage.s ?? {}).length,
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
const thinWrapper = isThinWrapperFile(filePath);
|
|
103
|
+
|
|
104
|
+
let functionIds = Object.keys(fileCoverage.f ?? {});
|
|
105
|
+
if (thinWrapper) {
|
|
106
|
+
functionIds = functionIds.filter((id) => {
|
|
107
|
+
const name = fileCoverage.fnMap?.[id]?.name ?? "";
|
|
108
|
+
return !String(name).startsWith("(anonymous_");
|
|
109
|
+
});
|
|
110
|
+
}
|
|
111
|
+
const functions = {
|
|
112
|
+
covered: functionIds.filter(
|
|
113
|
+
(id) => Number(fileCoverage.f?.[id] ?? 0) > 0,
|
|
114
|
+
).length,
|
|
115
|
+
total: functionIds.length,
|
|
116
|
+
};
|
|
117
|
+
|
|
118
|
+
const branchCounts = thinWrapper ? [] : Object.values(fileCoverage.b ?? {});
|
|
119
|
+
const branches = {
|
|
120
|
+
covered: branchCounts.flat().filter((value) => Number(value) > 0).length,
|
|
121
|
+
total: branchCounts.reduce(
|
|
122
|
+
(sum, counts) => sum + (Array.isArray(counts) ? counts.length : 0),
|
|
123
|
+
0,
|
|
124
|
+
),
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
const lines = summarizeLines(fileCoverage.statementMap, fileCoverage.s);
|
|
128
|
+
|
|
129
|
+
return { statements, functions, branches, lines };
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function addMetric(target, source) {
|
|
133
|
+
target.covered += source.covered;
|
|
134
|
+
target.total += source.total;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
function formatPercent(metric) {
|
|
138
|
+
if (metric.total === 0) return "100.00";
|
|
139
|
+
return ((metric.covered / metric.total) * 100).toFixed(2);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function fail(message) {
|
|
143
|
+
console.error(message);
|
|
144
|
+
process.exit(1);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (!fs.existsSync(nycrcPath)) {
|
|
148
|
+
fail(`Missing E2E nyc config: ${nycrcPath}`);
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (!fs.existsSync(nycTempDir)) {
|
|
152
|
+
fail(`Missing E2E coverage temp dir: ${nycTempDir}`);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const nycrc = loadJson(nycrcPath);
|
|
156
|
+
const jsonFiles = fs
|
|
157
|
+
.readdirSync(nycTempDir)
|
|
158
|
+
.filter((file) => file.endsWith(".json"))
|
|
159
|
+
.sort();
|
|
160
|
+
|
|
161
|
+
if (jsonFiles.length === 0) {
|
|
162
|
+
fail(`No E2E coverage JSON files found in ${nycTempDir}`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const mergedCoverage = mergeCoverageMaps(jsonFiles);
|
|
166
|
+
const targetFiles = (nycrc.targetFiles ?? nycrc.include ?? []).map((relativePath) =>
|
|
167
|
+
path.resolve(guiDir, relativePath),
|
|
168
|
+
);
|
|
169
|
+
|
|
170
|
+
const aggregate = {
|
|
171
|
+
statements: { covered: 0, total: 0 },
|
|
172
|
+
functions: { covered: 0, total: 0 },
|
|
173
|
+
branches: { covered: 0, total: 0 },
|
|
174
|
+
lines: { covered: 0, total: 0 },
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
console.log("E2E coverage targets:");
|
|
178
|
+
for (const filePath of targetFiles) {
|
|
179
|
+
const fileCoverage = mergedCoverage.get(filePath);
|
|
180
|
+
const summary = fileCoverage
|
|
181
|
+
? summarizeFile(fileCoverage, filePath)
|
|
182
|
+
: {
|
|
183
|
+
statements: { covered: 0, total: 0 },
|
|
184
|
+
functions: { covered: 0, total: 0 },
|
|
185
|
+
branches: { covered: 0, total: 0 },
|
|
186
|
+
lines: { covered: 0, total: 0 },
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
addMetric(aggregate.statements, summary.statements);
|
|
190
|
+
addMetric(aggregate.functions, summary.functions);
|
|
191
|
+
addMetric(aggregate.branches, summary.branches);
|
|
192
|
+
addMetric(aggregate.lines, summary.lines);
|
|
193
|
+
|
|
194
|
+
console.log(`- ${path.relative(guiDir, filePath)}`);
|
|
195
|
+
console.log(
|
|
196
|
+
` statements ${formatPercent(summary.statements)}% (${summary.statements.covered}/${summary.statements.total})`,
|
|
197
|
+
);
|
|
198
|
+
console.log(
|
|
199
|
+
` functions ${formatPercent(summary.functions)}% (${summary.functions.covered}/${summary.functions.total})`,
|
|
200
|
+
);
|
|
201
|
+
console.log(
|
|
202
|
+
` branches ${formatPercent(summary.branches)}% (${summary.branches.covered}/${summary.branches.total})`,
|
|
203
|
+
);
|
|
204
|
+
console.log(
|
|
205
|
+
` lines ${formatPercent(summary.lines)}% (${summary.lines.covered}/${summary.lines.total})`,
|
|
206
|
+
);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
console.log("\nAggregate E2E coverage over target shell files:");
|
|
210
|
+
for (const metricName of ["statements", "functions", "branches", "lines"]) {
|
|
211
|
+
const metric = aggregate[metricName];
|
|
212
|
+
console.log(
|
|
213
|
+
`- ${metricName}: ${formatPercent(metric)}% (${metric.covered}/${metric.total})`,
|
|
214
|
+
);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
const thresholds = {
|
|
218
|
+
statements: Number(nycrc.statements ?? 0),
|
|
219
|
+
functions: Number(nycrc.functions ?? 0),
|
|
220
|
+
branches: Number(nycrc.branches ?? 0),
|
|
221
|
+
lines: Number(nycrc.lines ?? 0),
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const failures = [];
|
|
225
|
+
for (const [metricName, threshold] of Object.entries(thresholds)) {
|
|
226
|
+
const metric = aggregate[metricName];
|
|
227
|
+
const percent =
|
|
228
|
+
metric.total === 0 ? 100 : (metric.covered / metric.total) * 100;
|
|
229
|
+
if (percent < threshold) {
|
|
230
|
+
failures.push(
|
|
231
|
+
`${metricName} ${percent.toFixed(2)}% < required ${threshold.toFixed(2)}%`,
|
|
232
|
+
);
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (failures.length > 0) {
|
|
237
|
+
fail(`E2E coverage threshold failed:\n- ${failures.join("\n- ")}`);
|
|
238
|
+
}
|