The allow list in this file never took effect. Claude Code discards every
`permissions.allow` entry from project settings while the workspace has
not been trusted (hasTrustDialogAccepted is false for this repo), and it
does so silently in interactive sessions. `Bash(*)` was therefore inert.
Removing it rather than granting trust: the rule only ever bites on the
unsandboxed path, where it would have meant "any command, outside the
sandbox, without a prompt" for everyone who clones this repo. Sandboxed
Bash is auto-allowed anyway, so the entry bought nothing safe.
Add ask rules for git commit and git push instead. Unlike allow rules,
ask and deny are not filtered by the trust gate, and the sandbox
auto-allow path checks them before granting, so they apply to sandboxed
and unsandboxed invocations alike. Both a bare and a wildcard form are
listed because `git commit *` compiles to /^git commit .*$/ and would
not match the bare command.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
datetime.now() returned a naive timestamp, so `generated_at` in the code
scanning report carried no offset and a reader could not tell which zone
it was written in. Use datetime.now(timezone.utc) for both the report
field and the fallback output directory name.
The changelog keeps date.today(): a release entry carries the author's
local calendar day, so UTC would be wrong there. The rule is suppressed
with that reason instead.
This is the last lint finding; `ruff check src tests` now passes.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Replace 46 blind `except Exception` handlers with the exception types the
guarded code can actually raise, so an unforeseen failure surfaces instead
of being silently swallowed. The types were derived per site from the try
block: file reads get (OSError, UnicodeDecodeError), TOML and YAML parsing
add their decode errors, int() guards get ValueError, imports get
ImportError, subprocess calls get (OSError, subprocess.SubprocessError),
git queries get GitRunError, and importlib.metadata lookups get
PackageNotFoundError.
Three handlers stay blind, now with a stated reason: the per-repository
loops in install and update are batch boundaries where one failing
repository must never abort the run.
Three imports come along because the narrowing needs them: GitRunError in
the changelog and version commands, and http.client in token validation --
urllib raises HTTPException, which is not an OSError subclass and would
otherwise have escaped.
Add tests/integration/test_error_path_degradation.py, which drives 44 of
the 46 handlers into their except branch and asserts the documented
degradation. Faults are injected for real rather than mocked wherever
possible: undecodable bytes, malformed YAML, digit strings past
sys.get_int_max_str_digits(), a real HTTPError with an unreadable body.
That approach paid for itself immediately -- tomllib.load() decodes
internally and raises UnicodeDecodeError, which was missing from the
pyproject handlers and would have turned a malformed pyproject.toml into
a traceback.
The two handlers left uncovered are the branch-close path in _release_impl,
reachable only through a full release, and the jinja2 import guard.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The resolver caught bare `Exception` around keyring reads and writes.
That could not be narrowed at the call site: the errors originate in
python-keyring, an optional dependency, so `keyring.errors.KeyringError`
is not referenceable without importing the package unconditionally.
Move the open-ended catch to the boundary where keyring is already
imported. KeyringTokenProvider.get/set now wrap the backend calls and
re-raise as KeyringOperationError, preserving the original exception as
__cause__. The resolver catches that concrete type instead.
KeyringOperationError is deliberately distinct from KeyringUnavailableError:
"no usable backend" and "backend present but the call failed" are different
states, and the resolver treats them differently — the former warns once and
visibly, the latter degrades silently.
Both degradation paths are unchanged: a failed write still lets the token
be used for the current run, a failed read still falls through to the prompt.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Repository-wide mechanical cleanup so `ruff check src tests` has a chance
of passing; no behavioural changes.
- Add `from __future__ import annotations` where PEP 604 unions are used.
This has to come first: pyproject declares requires-python >= 3.9, where
`X | None` is not evaluable at runtime unless annotations are stringified.
- Replace typing.List/Dict/Tuple/Set with the builtin generics and
Optional[X] with X | None, then drop the imports that became unused.
The four actions/*/__init__.py files needed this by hand because ruff
leaves unused imports in __init__.py alone (possible re-exports).
- Strip shebangs from 72 importable modules. None of them are executable
or invoked directly; the entry points are console_scripts and runpy.
- Flatten nested `with` blocks, collapse needless-bool returns, and apply
the remaining mechanical ruff fixes (PIE810, FLY002, PERF102, FURB192,
RUF059, I001).
- Pass check=False explicitly to the four subprocess.run() calls that
inspect returncode themselves. That is the existing default.
Two rewrites are visible to mocks, so their tests move with them:
subprocess.run(stdout=PIPE, stderr=PIPE) became capture_output=True, and
open(path, "r", ...) lost the redundant mode.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`make lint` runs ruff on src and tests directly on the host, using the
same invocation as the lint-python workflow so local and CI agree. The
script fails with a clear install hint when ruff is not on PATH.
`make test` now depends on `lint`, so linting runs before the container
images are built.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Repository-wide formatting pass over src/ and tests/: sorted imports and
__all__ entries, dropped redundant `# -*- coding: utf-8 -*-` headers,
unquoted forward-reference annotations (safe under
`from __future__ import annotations`), merged nested `with` statements,
moved `Iterable` and friends from `typing` to `collections.abc`, and
removed `# noqa: F401` markers that no longer suppress anything.
No behavioural changes. Unit and integration suites show the same four
pre-existing failures before and after the pass.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`git init` without `-b` inherits the ambient `init.defaultBranch` config,
which is `master` unless the host has configured otherwise, so `pkgmgr
repos create` produced master-based repositories. On top of that, the
push fallback renamed an already-correct `main` branch back to `master`
whenever the initial push failed (missing remote, auth error).
`init()` now requires an explicit `initial_branch` and runs
`git init -b <branch>`; the bootstrapper passes `main` and no longer
falls back to `master` on push failure.
`resolve_base_branch()` keeps its main -> master fallback, since it reads
existing repositories that may legitimately use master.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
pkgmgr code-scanning fetches a repository's GitHub code scanning alerts (and analysis metadata) via the gh CLI and writes alerts.json, a readable summary.md digest, and analyses.json into a timestamped directory (default /tmp/<repo>/code-scanner/<YYYYMMDDHHMMSS>) for offline analysis. The repository is resolved from gh or --repo; authentication is delegated to gh; --state filters by alert state.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The release flow now sanitises the release message into the changelog house style and validates it before writing: a leading '#' heading becomes a bold line, inline `code` becomes *italic*, and the resulting entry is checked with markdownlint-cli2 (using the target repo's own config, i.e. the same rules it enforces) or a built-in fallback. In an interactive run the editor re-opens with the findings until the entry is clean; with --message it transforms then aborts on any finding. The editor's ignore-marker moved from '#' to ';' so heading lines survive as content.
debian/changelog and the RPM %changelog mirror the transformed body, keeping all three changelogs consistent.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
update_debian_changelog and update_spec_changelog wrapped the whole release message in a single prefixed line, so only the first line of a multi-line (already-bulleted) message was indented/dashed while the rest stayed at column 0. That made dpkg-parsechangelog emit 'badly formatted heading line' and rpmspec report 'bad date', failing the package build. Each non-empty body line is now indented (Debian) or dash-prefixed (RPM) individually.
update_changelog no longer auto-prepends a '* ' bullet to the CHANGELOG.md entry: the message is inserted verbatim with blank-line separation, so a caller-provided markdown list stays markdown-lint-clean instead of gaining a stray leading bullet.
Adds regression tests for the multi-line debian/rpm mirroring and the no-injected-bullet behaviour.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
`pkgmgr install infinito` (and `pkgmgr path infinito`, `pkgmgr version
infinito`, etc.) failed with:
Identifier 'infinito' did not match any repository in config.
The infinito-nexus/core entry in defaults.yaml had no alias, so the
resolver could only match it via the full id `github.com/infinito-nexus/core`
or the bare repository name `core`. Downstream consumers (the
infinito-nexus-core Dockerfile, roles/sys-cli, roles/web-app-navigator,
and the test-install-pkgmgr CI job) all invoke the short identifier
`infinito` and broke once kpmx 1.15.x was rolled out via the floating
`pkgmgr-<distro>:stable` images.
Registering `alias: infinito` on the entry restores the short identifier
without renaming the repository or touching the consumer side.
update_changelog used to blindly prepend the new ## [version] entry to
the entire file body. When CHANGELOG.md leads with a # Changelog H1
the result was:
## [new] - YYYY-MM-DD
...
# Changelog
## [old] - ...
which trips markdownlint MD041 (first-line-h1) and MD012
(no-multiple-blanks), and reads as if the document were two stacked
changelogs.
The new _insert_after_h1 helper:
* Synthesises # Changelog when the file is empty.
* Inserts the entry between the existing H1 (plus any intro prose)
and the first existing ## release entry.
* For legacy headerless files (file starts with ##) prepends the
synthesised H1 + entry, so the resulting document is also
MD041-clean.
Tests cover the three layouts (empty, H1+existing-entries,
legacy-headerless). All 61 release-unit tests stay green.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Walks a directory of numbered NNN-topic.md files, promotes every file
with zero unchecked task-list items into the directorys README under
the ## Archive section, then deletes the source file. Keeps spec
directories (typically docs/requirements) short and focused on open
work.
The action ships as pkgmgr.actions.archive with four leaf modules
(discovery, inspect, readme, workflow) and is wired into the CLI as
pkgmgr archive [DIR] [--readme PATH] [--dry-run] [--include-template].
Extracted verbatim from cli/contributing/requirements/archive in
infinito-nexus-core so every kpmx-managed repository can rely on the
same archival convention without copy-pasting helpers. Twenty unit
tests cover discovery, inspection, README merge, and end-to-end
workflow paths.
Also: realign tests/unit/pkgmgr/cli/commands/test_release.py with the
new run_release(retry=False) signature shipped in v1.14.0.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Recovers from a release whose tag+commit landed cleanly but whose
post-tag steps (git push, latest-tag bump, twine upload) failed
mid-flight. pkgmgr release --retry skips the version bump, file
rewrites, commit, and tag-creation steps and re-runs only the
idempotent tail: re-push the existing HEAD tag, re-align the floating
latest tag, and (unless --no-publish) re-invoke publish.
The retry logic lives in its own module pkgmgr.actions.release.retry
so the workflow.py orchestrator stays focused on the forward path.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Previously the release workflow derived the distro-package name from
`os.path.basename(repo_root)`. Renaming the repo folder (e.g.
`infinito-nexus` → `infinito-nexus-core`) silently rewrote
`debian/changelog`'s top entry to the new folder name while
`debian/control` still pinned the legacy `Package:` value. dpkg-source
refuses to build a source package when the two disagree.
Add `resolve_package_name(paths)` that consults the existing packaging
files in priority order (debian/control `Package:` → PKGBUILD
`pkgname=` → RPM `.spec` `Name:`) and only falls back to the folder
basename when no packaging metadata is present. Extend `RepoPaths`
with a `debian_control` slot so `resolve_repo_paths` can discover the
file under both `packaging/debian/control` and the legacy `debian/`
location.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
`su -` runs through pam_systemd on Manjaro/Arch, creating a new login
session that conflicts with the outer sudo session and detaches the
install from the controlling terminal — making `sudo make install`
appear to end while it keeps running in the background. Replace `su`
calls with `runuser`, which is designed for root-invoked scripts and
skips PAM session management.
Also flips init.sh's non-root branch from `exit 0` (silent success) to
`exit 1` with a clear stderr message, so `make install` correctly fails
when invoked without root.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Drops the `analysis-ready-code` entry and renames the `infinito-nexus`
default to `infinito-nexus/core`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Adds `Bash(*)` to the allow list so routine shell commands run without
prompting.
- Sets `sandbox.failIfUnavailable=true` so Claude Code aborts rather
than silently running unsandboxed when the sandbox cannot initialize.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`dist/` carried in via the source tree can contain a stale wheel from a
previous build (e.g. kpmx-1.12.1 alongside the freshly built 1.13.3).
Both wheels declare a `bin/pkgmgr` entry, so `pypaInstallPhase` hits
FileExistsError on the second install. Wipe `dist/` in `preBuild` so
only the fresh wheel is installed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Adds `pkgmgr pull -j N` and `pkgmgr push -j N` for concurrent operation
across repositories (default: min(cpu_count, 8), use 1 for sequential).
Verification in pull also parallelizes; interactive prompts and the
actual git command still run on the main thread. Shared parallel-runner
and repo-resolution helpers live in a new `_parallel.py` module.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Introduce explicit concurrency settings to the CI and mark-stable
workflows to serialize runs per repository and ref. This prevents
overlapping executions for the same branch or tag and makes pipeline
behavior more predictable during rapid pushes.
https://chatgpt.com/share/6988bef0-1a0c-800f-93df-7a6c1bdc0331
Stop running the mark-stable workflow on v* tag pushes so it executes
only on branch updates. This prevents duplicate or unintended runs
after version tags are created as part of the release process.
https://chatgpt.com/share/6988bef0-1a0c-800f-93df-7a6c1bdc0331
Push master and the newly created version tag in a single git push command
so the CI release workflow can detect the tag on HEAD. This aligns the
release script with the new master-based release pipeline and prevents
missed automated releases caused by separate branch and tag pushes.
https://chatgpt.com/share/6988bef0-1a0c-800f-93df-7a6c1bdc0331
- add dedicated `slim` Dockerfile stage based on `full`
- move image cleanup into slim stage via slim.sh
- extend build script to support `--target slim`
- publish pkgmgr-*-slim images for all distros
https://chatgpt.com/share/69701a4e-b000-800f-be7e-162dcb93b1d2
Show the working directory and captured git output when `git pull`
fails via pull_args(). This makes debugging repository-specific
failures (missing upstream, auth issues, detached HEAD, etc.)
significantly easier, especially when pulling multiple repositories.
https://chatgpt.com/share/6969ff2c-ed2c-800f-b506-5834b6b81141
Arch Linux ARM currently ships a broken/out-of-sync nix package with
unresolvable dependencies. Declare nix as a hard dependency only on
x86_64 and as optional on other architectures, allowing installation
while relying on the official Nix installer bootstrap.
https://chatgpt.com/share/695e483c-1f68-800f-9f94-87d5295b871d
- Ship default YAML configs inside the pkgmgr package
- Ensure defaults are loaded when no user config exists
- Keep user configs fully respected and non-overwritten
- Fix config update command to copy packaged defaults reliably
https://chatgpt.com/share/6947e74f-573c-800f-b93d-5ed341fcd1a3
- Restrict setuptools package discovery to src/ (pkgmgr* only)
- Drop config/ as a Python package mapping (keep config as plain data dir)
- Remove config_defaults fallback paths and use config/ exclusively
- Add unit + integration tests for defaults.yaml loading and CLI update copying
https://chatgpt.com/share/6947e74f-573c-800f-b93d-5ed341fcd1a3
* Add mirror visibility subcommand and provision --public flag
* Implement core visibility API with provider support (GitHub, Gitea)
* Extend provider interface and EnsureStatus
* Add unit, integration and e2e tests for visibility handling
https://chatgpt.com/share/6946a44e-4f48-800f-8124-9c0b9b2b6b04
- Add probe_remote_reachable_detail and improved GitRunError metadata
- Print short failure reasons for unreachable remotes
- Provision each git mirror URL via ensure_remote_repository_for_url
https://chatgpt.com/share/6946956e-f738-800f-a446-e2c8bf5595f4
- Adjust tests to expect RuntimeError instead of SystemExit
- Add coverage for missing [project] section in pyproject.toml
- Keep spec macro %{?dist} intact in test fixtures
- Minor cleanup and reformatting of test cases
https://chatgpt.com/share/69454836-4698-800f-9d19-7e67e8e789d6
Update version handling to correctly modify [project].version in pyproject.toml.
The previous implementation only matched top-level version assignments and
failed for PEP 621 layouts.
- Restrict update to the [project] section
- Allow leading whitespace in version lines
- Replace sys.exit() with proper exceptions
- Remove unused sys import
https://chatgpt.com/share/69454836-4698-800f-9d19-7e67e8e789d6
- Add NIX_CONFIG with GitHub access token to all CI test workflows
- Export NIX_CONFIG in Makefile for propagation to test scripts
- Forward NIX_CONFIG explicitly into all docker run invocations
- Prevent GitHub API rate limit errors during Nix-based tests
https://chatgpt.com/share/69432655-a948-800f-8c0d-353921cdf644
* Replace legacy GitError usage with a clearer exception hierarchy:
* GitBaseError as the common root for all git-related failures
* GitRunError for subprocess execution failures
* GitQueryError for read-only query failures
* GitCommandError for state-changing command failures
* GitNotRepositoryError to explicitly signal “not a git repository” situations
* Update git runner to detect “not a git repository” stderr and raise GitNotRepositoryError with rich context (cwd, command, stderr)
* Refactor repository verification to use dedicated query helpers instead of ad-hoc subprocess calls:
* get_remote_head_commit (ls-remote) for pull mode
* get_head_commit for local mode
* get_latest_signing_key (%GK) for signature verification
* Add strict vs best-effort behavior in verify_repository:
* Best-effort collection for reporting (does not block when no verification config exists)
* Strict retrieval and explicit error messages when verification is configured
* Clear failure cases when commit/signing key cannot be determined
* Add new unit tests covering:
* get_latest_signing_key output stripping and error wrapping
* get_remote_head_commit parsing, empty output, and error wrapping
* verify_repository success/failure scenarios and “do not swallow GitNotRepositoryError”
* Adjust imports and exception handling across actions/commands/queries to align with GitRunError-based handling while keeping GitNotRepositoryError uncaught for debugging clarity
https://chatgpt.com/share/6943173c-508c-800f-8879-af75d131c79b
Standardize Docker/CI/test environments to mount pkgmgr at /opt/src/pkgmgr.
This makes the layering explicit: pkgmgr is the lower-level foundation used by
Infinito.Nexus.
Infra-only change (Docker, CI, shell scripts). No runtime or Nix semantics changed.
https://chatgpt.com/share/69427fe7-e288-800f-90a4-c1c3c11a8484
- Allow MIRRORS to contain plain URLs (one per line) in addition to legacy "NAME URL"
- Treat strings as single URLs to avoid iterable pitfalls
- Write PyPI URLs as metadata-only entries (never added to git config)
- Keep MIRRORS as the single source of truth for mirror setup
- Update integration test to assert URL-only MIRRORS output
https://chatgpt.com/share/6941a9aa-b8b4-800f-963d-2486b34856b1
* Move VS Code workspace logic (incl. guards) from cli.commands.tools into cli.tools.vscode
* Extract shared repo path resolution into cli.tools.paths and reuse for explore/terminal
* Simplify cli.commands.tools to pure orchestration via open_vscode_workspace
* Update existing tools command unit test to assert delegation instead of patching removed internals
* Add new unit tests for cli.tools.paths and cli.tools.vscode (workspace creation, reuse, guard errors)
https://chatgpt.com/share/69419a6a-c9e4-800f-9538-b6652b2da6b3
- Treat MIRRORS as the only authority for mirror URLs
- Filter non-git URLs (e.g. PyPI) from git remotes and push URLs
- Prefer SSH git URLs when determining primary origin
- Ensure mirror probing only targets valid git remotes
- Refactor repository create into service-based architecture
- Write PyPI metadata exclusively to MIRRORS, never to git config
- Add integration test verifying PyPI is not written into .git/config
- Update preview and unit tests to match new create flow
https://chatgpt.com/share/69415c61-1c5c-800f-86dd-0405edec25db
- Reclassify create repo preview test as integration test
- Rename test class to drop E2E naming
- Replace subprocess mock with core.git command mocks (init/add_all/commit)
- Patch get_config_value to avoid git config dependency
https://chatgpt.com/share/694150de-873c-800f-a01d-df3cc7ce25df