Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 9 additions & 1 deletion docs/src/content/docs/guides/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,15 @@ dependencies:
alias: review # local alias (controls install directory name)
```

Fields: `git` (required), `path`, `ref`, `alias` (all optional). The `git` value is any HTTPS or SSH clone URL.
Fields: `git` (required), `path`, `ref`, `alias` (all optional). The `git` value is any HTTPS, HTTP or SSH clone URL.

:::caution
Use HTTP dependencies only on trusted private networks. Declare them with
`git: http://...` and `allow_insecure: true` in `apm.yml`. Installing them
still requires `apm install --allow-insecure`, unless
`apm config set allow-insecure true` is enabled globally.
:::


> **Nested groups (GitLab, Gitea, etc.):** APM treats all path segments after the host as the repo path, so `gitlab.com/group/subgroup/repo` resolves to a repo at `group/subgroup/repo`. Virtual paths on simple 2-segment repos work with shorthand (`gitlab.com/owner/repo/file.prompt.md`). But for **nested-group repos + virtual paths**, use the object format — the shorthand is ambiguous:
>
Expand Down
21 changes: 17 additions & 4 deletions docs/src/content/docs/reference/cli-commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ apm init my-plugin --plugin

### `apm install` - Install dependencies and deploy local content

Install APM package and MCP server dependencies from `apm.yml` and deploy the project's own `.apm/` content to target directories (like `npm install`). Auto-creates minimal `apm.yml` when packages are specified but no manifest exists.
Install APM package and MCP server dependencies from `apm.yml` and deploy the project's own `.apm/` content to target directories (like `npm install`). Auto-creates minimal `apm.yml` when packages are specified but no manifest exists. For `http://` dependencies, use `--allow-insecure` or enable the global `allow-insecure` config.

```bash
apm install [PACKAGES...] [OPTIONS]
Expand All @@ -96,6 +96,7 @@ apm install [PACKAGES...] [OPTIONS]
- `--trust-transitive-mcp` - Trust self-defined MCP servers from transitive packages (skip re-declaration requirement)
- `--dev` - Add packages to [`devDependencies`](../manifest-schema/#5-devdependencies) instead of `dependencies`. Dev deps are installed locally but excluded from `apm pack --format plugin` bundles
- `-g, --global` - Install to user scope (`~/.apm/`) instead of the current project. Primitives deploy to `~/.copilot/`, `~/.claude/`, etc.
- `--allow-insecure` - Allow HTTP (insecure) dependencies. Required when adding or installing dependencies that use an `http://` URL.

**Behavior:**
- `apm install` (no args): Installs **all** packages from `apm.yml` and deploys the project's own `.apm/` content
Expand Down Expand Up @@ -1363,6 +1364,7 @@ apm config get [KEY]
**Arguments:**
- `KEY` (optional) - Configuration key to retrieve. Supported keys:
- `auto-integrate` - Whether to automatically integrate `.prompt.md` files into AGENTS.md
- `allow-insecure` - Whether HTTP (insecure) dependencies are allowed globally

If `KEY` is omitted, displays all configuration values.

Expand All @@ -1371,6 +1373,9 @@ If `KEY` is omitted, displays all configuration values.
# Get auto-integrate setting
apm config get auto-integrate

# Get allow-insecure setting
apm config get allow-insecure

# Show all configuration
apm config get
```
Expand All @@ -1386,6 +1391,7 @@ apm config set KEY VALUE
**Arguments:**
- `KEY` - Configuration key to set. Supported keys:
- `auto-integrate` - Enable/disable automatic integration of `.prompt.md` files
- `allow-insecure` - Allow HTTP (insecure) dependencies globally
- `VALUE` - Value to set. For boolean keys, use: `true`, `false`, `yes`, `no`, `1`, `0`

**Configuration Keys:**
Expand All @@ -1398,6 +1404,11 @@ apm config set KEY VALUE
- Set to `false` if you want to manually manage which prompts are compiled
- Set to `true` to ensure all prompts are always included in the context

**`allow-insecure`** - Allow HTTP (insecure) dependencies globally
- **Type:** Boolean
- **Default:** `false`
- **Description:** When enabled, APM skips the requirement to pass `--allow-insecure` at install time for HTTP dependencies. The per-dependency `allow_insecure: true` field in apm.yml is still required. Use this for private network environments where all servers use HTTP.

**Examples:**
```bash
# Enable auto-integration (default)
Expand All @@ -1406,9 +1417,11 @@ apm config set auto-integrate true
# Disable auto-integration
apm config set auto-integrate false

# Using alternative boolean values
apm config set auto-integrate yes
apm config set auto-integrate 1
# Allow HTTP dependencies globally (skips --allow-insecure flag requirement)
apm config set allow-insecure true

# Disable globally (default)
apm config set allow-insecure false
```

## Runtime Management (Experimental)
Expand Down
2 changes: 1 addition & 1 deletion packages/apm-guide/.apm/skills/apm-usage/commands.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@

| Command | Purpose | Key flags |
|---------|---------|-----------|
| `apm install [PKGS...]` | Install packages | `--update` refresh refs, `--force` overwrite, `--dry-run`, `--verbose`, `--only [apm\|mcp]`, `--target`, `--dev`, `-g` global, `--trust-transitive-mcp`, `--parallel-downloads N` |
| `apm install [PKGS...]` | Install packages | `--update` refresh refs, `--force` overwrite, `--dry-run`, `--verbose`, `--only [apm\|mcp]`, `--target`, `--dev`, `-g` global, `--trust-transitive-mcp`, `--parallel-downloads N`, `--allow-insecure` |
| `apm uninstall PKGS...` | Remove packages | `--dry-run`, `-g` global |
| `apm prune` | Remove orphaned packages | `--dry-run` |
| `apm deps list` | List installed packages | `-g` global, `--all` both scopes |
Expand Down
3 changes: 3 additions & 0 deletions src/apm_cli/bundle/plugin_exporter.py
Original file line number Diff line number Diff line change
Expand Up @@ -391,8 +391,11 @@ def _dep_install_path(dep: LockedDependency, apm_modules_dir: Path) -> Path:
host=dep.host,
virtual_path=dep.virtual_path,
is_virtual=dep.is_virtual,
artifactory_prefix=dep.registry_prefix,
is_local=(dep.source == "local"),
local_path=dep.local_path,
is_insecure=dep.is_insecure,
allow_insecure=dep.allow_insecure,
)
return dep_ref.get_install_path(apm_modules_dir)

Expand Down
5 changes: 5 additions & 0 deletions src/apm_cli/commands/_helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,11 @@ def _build_expected_install_paths(declared_deps, lockfile, apm_modules_dir: Path
host=dep.host,
virtual_path=dep.virtual_path,
is_virtual=dep.is_virtual,
artifactory_prefix=dep.registry_prefix,
is_local=(dep.source == "local"),
local_path=dep.local_path,
is_insecure=dep.is_insecure,
allow_insecure=dep.allow_insecure,
)
install_path = dep_ref.get_install_path(apm_modules_dir)
try:
Expand Down
95 changes: 70 additions & 25 deletions src/apm_cli/commands/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,48 @@
# Restore builtin since a subcommand is named ``set``
set = builtins.set

_BOOLEAN_TRUE_VALUES = {"true", "1", "yes"}
_BOOLEAN_FALSE_VALUES = {"false", "0", "no"}
_CONFIG_KEY_DISPLAY_NAMES = {
"auto_integrate": "auto-integrate",
"allow_insecure": "allow-insecure",
}


def _parse_bool_value(value: str) -> bool:
"""Parse a CLI boolean value."""
normalized = value.strip().lower()
if normalized in _BOOLEAN_TRUE_VALUES:
return True
if normalized in _BOOLEAN_FALSE_VALUES:
return False
raise ValueError(f"Invalid value '{value}'. Use 'true' or 'false'.")
Comment thread
arika0093 marked this conversation as resolved.


def _get_config_setters():
"""Return config setters keyed by CLI option name."""
from ..config import set_auto_integrate, set_allow_insecure

return {
"auto-integrate": (set_auto_integrate, "Auto-integration"),
"allow-insecure": (set_allow_insecure, "Allow-insecure"),
}


def _get_config_getters():
"""Return config getters keyed by CLI option name."""
from ..config import get_auto_integrate, get_allow_insecure

return {
"auto-integrate": get_auto_integrate,
"allow-insecure": get_allow_insecure,
}


def _valid_config_keys() -> str:
"""Return valid config keys for messages."""
return ", ".join(_get_config_getters().keys())


@click.group(help="Configure APM CLI", invoke_without_command=True)
@click.pass_context
Expand Down Expand Up @@ -115,28 +157,32 @@ def set(key, value):
Examples:
apm config set auto-integrate false
apm config set auto-integrate true
apm config set allow-insecure true
"""
from ..config import set_auto_integrate

logger = CommandLogger("config set")
if key == "auto-integrate":
if value.lower() in ["true", "1", "yes"]:
set_auto_integrate(True)
logger.success("Auto-integration enabled")
elif value.lower() in ["false", "0", "no"]:
set_auto_integrate(False)
logger.success("Auto-integration disabled")
else:
logger.error(f"Invalid value '{value}'. Use 'true' or 'false'.")
sys.exit(1)
else:
setters = _get_config_setters()
config_entry = setters.get(key)
if config_entry is None:
logger.error(f"Unknown configuration key: '{key}'")
logger.progress("Valid keys: auto-integrate")
logger.progress(f"Valid keys: {_valid_config_keys()}")
logger.progress(
"This error may indicate a bug in command routing. Please report this issue."
)
sys.exit(1)

try:
enabled = _parse_bool_value(value)
except ValueError as exc:
logger.error(str(exc))
sys.exit(1)

setter, label = config_entry
setter(enabled)
if enabled:
logger.success(f"{label} enabled")
else:
logger.success(f"{label} disabled")


@config.command(help="Get a configuration value")
@click.argument("key", required=False)
Expand All @@ -145,29 +191,28 @@ def get(key):

Examples:
apm config get auto-integrate
apm config get allow-insecure
apm config get
"""
from ..config import get_config, get_auto_integrate
from ..config import get_config

logger = CommandLogger("config get")
getters = _get_config_getters()
if key:
if key == "auto-integrate":
value = get_auto_integrate()
click.echo(f"auto-integrate: {value}")
else:
getter = getters.get(key)
if getter is None:
logger.error(f"Unknown configuration key: '{key}'")
logger.progress("Valid keys: auto-integrate")
logger.progress(f"Valid keys: {_valid_config_keys()}")
logger.progress(
"This error may indicate a bug in command routing. Please report this issue."
)
sys.exit(1)
value = getter()
click.echo(f"{key}: {value}")
else:
# Show all config
config_data = get_config()
logger.progress("APM Configuration:")
for k, v in config_data.items():
# Map internal keys to user-friendly names
if k == "auto_integrate":
click.echo(f" auto-integrate: {v}")
else:
click.echo(f" {k}: {v}")
display_key = _CONFIG_KEY_DISPLAY_NAMES.get(k, k)
click.echo(f" {display_key}: {v}")
Loading