ws-aitoolkit 0.1.3__tar.gz → 0.1.4__tar.gz
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.
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/PKG-INFO +1 -1
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/pyproject.toml +1 -1
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/pyproject.toml.orig +1 -1
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/_build.py +2 -2
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/syntax_check.py +17 -1
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/policy.py +25 -4
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/LICENSE +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/NOTICE +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/README.md +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/__init__.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/__main__.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/adapters/__init__.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/adapters/claude_code.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/adapters/opencode.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/adapters/opencode_plugin.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/approvals.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/broker.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/canary.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/cli.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/config_cmd.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/devices.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/doctor.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/eval.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/evalstats.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/extcmd.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/frontmatter.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/gate.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/globals.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/__init__.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/_paths.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/bulk_staging.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/claims.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/record_calls.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/session_context.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/skill_gate.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/spawn_cap.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/guards/test_weakening.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/install.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/jobs.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/jsonl.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/mcp_server.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/memory.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/modules.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/notes.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/notesdetect.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/osinfo.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/output.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/profile.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/rating.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/__init__.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/_schedule.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/anthropic.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/commands.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/launchd.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/markdown_git.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/notify_none.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/ntfy.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/opencode.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/restic.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/secrets_env.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/secrets_ws.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/sops_age.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/roles/systemd.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/run.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/runrecord.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/runschema.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/sandbox.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/secrets.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/selfcmd.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/session.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/setup_ui/__init__.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/setup_ui/brand.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/setup_ui/flow.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/setup_ui/inline.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/setup_ui/web.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/skills/__init__.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/skills/align.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/skills/checkpoint.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/skills/dataviz.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/skills/resume.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/skills/ship.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/skills/taste_gate.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/status.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/testing/__init__.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/testing/mockprovider.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/testing/scenarios.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/usage.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/userconfig.py +0 -0
- {ws_aitoolkit-0.1.3 → ws_aitoolkit-0.1.4}/src/ws/version.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Written by the release workflow before uv build; absent in a checkout."""
|
|
2
2
|
|
|
3
|
-
git_sha = "
|
|
3
|
+
git_sha = "9ed7a1d78df76dd7e28498dd93f03890e6b1efb1"
|
|
4
4
|
dirty = False
|
|
5
|
-
build_date = "2026-11-
|
|
5
|
+
build_date = "2026-11-01T15:26:17Z"
|
|
6
6
|
source = "release"
|
|
@@ -104,6 +104,22 @@ def _run(argv: list[str], path: str) -> tuple[str | None, str | None]:
|
|
|
104
104
|
return f"{path}: {argv[0]} found a problem:\n{_brief(done.stdout + done.stderr)}", None
|
|
105
105
|
|
|
106
106
|
|
|
107
|
+
def _bash_shebang(path: str) -> bool:
|
|
108
|
+
"""Does the script ask for bash on its first line (`#!/bin/bash`, `#!/usr/bin/env bash`)? Then `sh -n` would
|
|
109
|
+
report bash syntax such as `>(...)` as an error."""
|
|
110
|
+
try:
|
|
111
|
+
with open(path, "rb") as f:
|
|
112
|
+
first = f.readline(256)
|
|
113
|
+
except OSError:
|
|
114
|
+
return False
|
|
115
|
+
if not first.startswith(b"#!"):
|
|
116
|
+
return False
|
|
117
|
+
words = first[2:].split()
|
|
118
|
+
if words and words[0].endswith(b"/env"):
|
|
119
|
+
words = [w for w in words[1:] if not w.startswith(b"-")]
|
|
120
|
+
return bool(words) and words[0].rsplit(b"/", 1)[-1] == b"bash"
|
|
121
|
+
|
|
122
|
+
|
|
107
123
|
def judge(call, cfg: Config, ctx) -> Finding | None:
|
|
108
124
|
path = os.path.abspath(os.path.join(ctx.cwd or "", call.value))
|
|
109
125
|
ext = path.rsplit(".", 1)[-1].lower() if "." in os.path.basename(path) else ""
|
|
@@ -118,7 +134,7 @@ def judge(call, cfg: Config, ctx) -> Finding | None:
|
|
|
118
134
|
data = f.read()
|
|
119
135
|
problem, note = {"py": _python, "json": _json, "toml": _toml}[ext](path, data), None
|
|
120
136
|
elif ext in ("sh", "bash"):
|
|
121
|
-
shell = "bash" if ext == "bash" else "sh"
|
|
137
|
+
shell = "bash" if ext == "bash" or _bash_shebang(path) else "sh"
|
|
122
138
|
if shutil.which(shell) is None and shutil.which("bash") is not None:
|
|
123
139
|
shell = "bash"
|
|
124
140
|
problem, note = _run([shell, "-n"], path)
|
|
@@ -1131,6 +1131,28 @@ def _cluster(flags: list[str], letter: str) -> bool:
|
|
|
1131
1131
|
# --- paths: deny_read, protected files ----------------------------------------------------------------------------
|
|
1132
1132
|
|
|
1133
1133
|
|
|
1134
|
+
def _glob_class(pattern: str, i: int) -> tuple[str, int] | None:
|
|
1135
|
+
"""The bracket class opening at `pattern[i]` as a regex class and the index of its `]`, or None when it is not
|
|
1136
|
+
one (no `]` before a `/`, or a range like `s-c` that runs backwards), so the `[` is matched as itself."""
|
|
1137
|
+
j = pattern.find("]", i + 2)
|
|
1138
|
+
if j < 0 or "/" in pattern[i + 1 : j]:
|
|
1139
|
+
return None
|
|
1140
|
+
body = pattern[i + 1 : j]
|
|
1141
|
+
neg = body[0] in "!^"
|
|
1142
|
+
body = body[1:] if neg else body
|
|
1143
|
+
parts, k = [], 0
|
|
1144
|
+
while k < len(body):
|
|
1145
|
+
if k + 2 < len(body) and body[k + 1] == "-":
|
|
1146
|
+
if body[k] > body[k + 2]:
|
|
1147
|
+
return None
|
|
1148
|
+
parts.append(re.escape(body[k]) + "-" + re.escape(body[k + 2]))
|
|
1149
|
+
k += 3
|
|
1150
|
+
else:
|
|
1151
|
+
parts.append(re.escape(body[k]))
|
|
1152
|
+
k += 1
|
|
1153
|
+
return "[" + ("^" if neg else "") + "".join(parts) + "]", j
|
|
1154
|
+
|
|
1155
|
+
|
|
1134
1156
|
def _glob_regex_path(pattern: str) -> re.Pattern:
|
|
1135
1157
|
out, i = [], 0
|
|
1136
1158
|
while i < len(pattern):
|
|
@@ -1147,10 +1169,9 @@ def _glob_regex_path(pattern: str) -> re.Pattern:
|
|
|
1147
1169
|
out.append("[^/]*")
|
|
1148
1170
|
elif c == "?":
|
|
1149
1171
|
out.append("[^/]")
|
|
1150
|
-
elif c == "[" and (
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
i = j
|
|
1172
|
+
elif c == "[" and (cls := _glob_class(pattern, i)) is not None:
|
|
1173
|
+
out.append(cls[0])
|
|
1174
|
+
i = cls[1]
|
|
1154
1175
|
else:
|
|
1155
1176
|
out.append(re.escape(c))
|
|
1156
1177
|
i += 1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|