devlift-cli 0.1.0__tar.gz → 0.1.2__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.
Files changed (66) hide show
  1. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/PKG-INFO +29 -13
  2. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/README.md +28 -12
  3. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/MANUAL.md +23 -0
  4. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/__init__.py +1 -1
  5. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/app.py +2 -0
  6. devlift_cli-0.1.2/devlift_cli/commands/skill.py +158 -0
  7. devlift_cli-0.1.2/devlift_cli/skill/SKILL.md +192 -0
  8. devlift_cli-0.1.2/devlift_cli/skill/reference/commands.md +162 -0
  9. devlift_cli-0.1.2/devlift_cli/skill/reference/exit-codes.md +16 -0
  10. devlift_cli-0.1.2/devlift_cli/skill/reference/workflows.md +105 -0
  11. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli.egg-info/PKG-INFO +29 -13
  12. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli.egg-info/SOURCES.txt +6 -1
  13. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/pyproject.toml +5 -1
  14. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/__main__.py +0 -0
  15. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/__init__.py +0 -0
  16. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/approvals.py +0 -0
  17. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/catalog.py +0 -0
  18. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/client.py +0 -0
  19. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/context.py +0 -0
  20. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/deployments.py +0 -0
  21. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/infra.py +0 -0
  22. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/infra_list.py +0 -0
  23. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/kong.py +0 -0
  24. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/services.py +0 -0
  25. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/api/vpc.py +0 -0
  26. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/auth/__init__.py +0 -0
  27. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/auth/oauth.py +0 -0
  28. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/auth/session.py +0 -0
  29. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/auth/storage.py +0 -0
  30. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/__init__.py +0 -0
  31. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/approval.py +0 -0
  32. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/auth.py +0 -0
  33. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/catalog.py +0 -0
  34. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/clusters.py +0 -0
  35. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/deployment.py +0 -0
  36. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/dynamodb.py +0 -0
  37. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/eks.py +0 -0
  38. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/kong.py +0 -0
  39. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/languages.py +0 -0
  40. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/manual.py +0 -0
  41. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/repositories.py +0 -0
  42. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/request.py +0 -0
  43. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/s3.py +0 -0
  44. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/commands/sqs.py +0 -0
  45. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/config.py +0 -0
  46. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/context.py +0 -0
  47. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/data/placement/vance.json +0 -0
  48. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/errors.py +0 -0
  49. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/ops/__init__.py +0 -0
  50. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/ops/approvals.py +0 -0
  51. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/ops/eks.py +0 -0
  52. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/ops/kong.py +0 -0
  53. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/ops/placement.py +0 -0
  54. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/ops/resources.py +0 -0
  55. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/ops/status.py +0 -0
  56. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/ops/wait.py +0 -0
  57. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/render/__init__.py +0 -0
  58. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/render/output.py +0 -0
  59. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/resolve/__init__.py +0 -0
  60. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/resolve/allowlist.py +0 -0
  61. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli/resolve/names.py +0 -0
  62. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli.egg-info/dependency_links.txt +0 -0
  63. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli.egg-info/entry_points.txt +0 -0
  64. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli.egg-info/requires.txt +0 -0
  65. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/devlift_cli.egg-info/top_level.txt +0 -0
  66. {devlift_cli-0.1.0 → devlift_cli-0.1.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devlift-cli
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: DevLift command-line tool: create, change, review and deploy DevLift resources from the terminal.
5
5
  License: Proprietary
6
6
  Keywords: devlift,devops,deployment,infrastructure,cli
@@ -40,17 +40,29 @@ the same endpoints the web dashboard uses. Nothing here talks to an LLM.
40
40
  ## Install
41
41
 
42
42
  ```
43
- pip install devlift-cli
43
+ pipx install devlift-cli
44
44
  ```
45
45
 
46
- Or, to keep it in its own environment and off your system Python:
46
+ pipx gives the tool its own environment and puts `devlift` and `dl` on your
47
+ PATH. If you do not have it:
47
48
 
48
49
  ```
49
- pipx install devlift-cli
50
+ sudo apt install pipx # Debian / Ubuntu
51
+ brew install pipx # macOS
52
+ pipx ensurepath # once, then reopen your terminal
50
53
  ```
51
54
 
52
- Upgrade with `pip install -U devlift-cli` (or `pipx upgrade devlift-cli`).
53
- Your profile and sign-in live in `~/.config/devlift` and survive upgrades.
55
+ Upgrade with `pipx upgrade devlift-cli`. Your profile and sign-in live in
56
+ `~/.config/devlift` and survive upgrades.
57
+
58
+ **Using pip instead?** Do it inside a virtual environment. A plain
59
+ `pip install devlift-cli` fails on recent Debian, Ubuntu and Fedora with
60
+ `externally-managed-environment`, because those systems refuse to let pip
61
+ touch the system Python.
62
+
63
+ ```
64
+ python3 -m venv ~/.venvs/devlift && ~/.venvs/devlift/bin/pip install devlift-cli
65
+ ```
54
66
 
55
67
  Then:
56
68
 
@@ -61,9 +73,9 @@ devlift whoami
61
73
  devlift man
62
74
  ```
63
75
 
64
- Requires Python 3.11 or newer. To remove it: `pip uninstall devlift-cli`
65
- (or `pipx uninstall devlift-cli`). That leaves `~/.config/devlift`, so delete
66
- that too if you want your profiles and sign-in gone as well.
76
+ Requires Python 3.11 or newer. To remove it: `pipx uninstall devlift-cli`.
77
+ That leaves `~/.config/devlift`, so delete that too if you want your profiles
78
+ and sign-in gone as well.
67
79
 
68
80
  Several backends? `devlift profile list`, `devlift profile use <name>` and
69
81
  `devlift profile show` switch between them and say which one is active.
@@ -92,14 +104,18 @@ Variables and secrets stay in the dashboard by design.
92
104
 
93
105
  ## Claude skill
94
106
 
95
- `skills/devlift/` teaches Claude (Claude Code or any Agent Skills host) to
96
- drive this CLI non-interactively. Install with:
107
+ A Claude skill ships inside the package, teaching Claude (Claude Code or any
108
+ Agent Skills host) to drive this CLI non-interactively. Install it with:
97
109
 
98
110
  ```
99
- ln -s "$(pwd)/skills/devlift" ~/.claude/skills/devlift # personal
100
- # or copy it into a project's .claude/skills/devlift
111
+ devlift skill install # ~/.claude/skills/devlift, every project
112
+ devlift skill install --project # ./.claude/skills/devlift, this repo only
101
113
  ```
102
114
 
115
+ Start a new Claude session afterwards to pick it up. `devlift skill status`
116
+ says where it is installed and whether it still matches the CLI;
117
+ `devlift skill install --force` refreshes it after an upgrade.
118
+
103
119
  ## Development
104
120
 
105
121
  Building, testing and cutting a release: see `CONTRIBUTING.md` in the source
@@ -12,17 +12,29 @@ the same endpoints the web dashboard uses. Nothing here talks to an LLM.
12
12
  ## Install
13
13
 
14
14
  ```
15
- pip install devlift-cli
15
+ pipx install devlift-cli
16
16
  ```
17
17
 
18
- Or, to keep it in its own environment and off your system Python:
18
+ pipx gives the tool its own environment and puts `devlift` and `dl` on your
19
+ PATH. If you do not have it:
19
20
 
20
21
  ```
21
- pipx install devlift-cli
22
+ sudo apt install pipx # Debian / Ubuntu
23
+ brew install pipx # macOS
24
+ pipx ensurepath # once, then reopen your terminal
22
25
  ```
23
26
 
24
- Upgrade with `pip install -U devlift-cli` (or `pipx upgrade devlift-cli`).
25
- Your profile and sign-in live in `~/.config/devlift` and survive upgrades.
27
+ Upgrade with `pipx upgrade devlift-cli`. Your profile and sign-in live in
28
+ `~/.config/devlift` and survive upgrades.
29
+
30
+ **Using pip instead?** Do it inside a virtual environment. A plain
31
+ `pip install devlift-cli` fails on recent Debian, Ubuntu and Fedora with
32
+ `externally-managed-environment`, because those systems refuse to let pip
33
+ touch the system Python.
34
+
35
+ ```
36
+ python3 -m venv ~/.venvs/devlift && ~/.venvs/devlift/bin/pip install devlift-cli
37
+ ```
26
38
 
27
39
  Then:
28
40
 
@@ -33,9 +45,9 @@ devlift whoami
33
45
  devlift man
34
46
  ```
35
47
 
36
- Requires Python 3.11 or newer. To remove it: `pip uninstall devlift-cli`
37
- (or `pipx uninstall devlift-cli`). That leaves `~/.config/devlift`, so delete
38
- that too if you want your profiles and sign-in gone as well.
48
+ Requires Python 3.11 or newer. To remove it: `pipx uninstall devlift-cli`.
49
+ That leaves `~/.config/devlift`, so delete that too if you want your profiles
50
+ and sign-in gone as well.
39
51
 
40
52
  Several backends? `devlift profile list`, `devlift profile use <name>` and
41
53
  `devlift profile show` switch between them and say which one is active.
@@ -64,14 +76,18 @@ Variables and secrets stay in the dashboard by design.
64
76
 
65
77
  ## Claude skill
66
78
 
67
- `skills/devlift/` teaches Claude (Claude Code or any Agent Skills host) to
68
- drive this CLI non-interactively. Install with:
79
+ A Claude skill ships inside the package, teaching Claude (Claude Code or any
80
+ Agent Skills host) to drive this CLI non-interactively. Install it with:
69
81
 
70
82
  ```
71
- ln -s "$(pwd)/skills/devlift" ~/.claude/skills/devlift # personal
72
- # or copy it into a project's .claude/skills/devlift
83
+ devlift skill install # ~/.claude/skills/devlift, every project
84
+ devlift skill install --project # ./.claude/skills/devlift, this repo only
73
85
  ```
74
86
 
87
+ Start a new Claude session afterwards to pick it up. `devlift skill status`
88
+ says where it is installed and whether it still matches the CLI;
89
+ `devlift skill install --force` refreshes it after an upgrade.
90
+
75
91
  ## Development
76
92
 
77
93
  Building, testing and cutting a release: see `CONTRIBUTING.md` in the source
@@ -875,6 +875,29 @@ the workflow reaches a terminal status.
875
875
  `--wait` follows the deployment to its end and then ArgoCD until the state
876
876
  settles (up to ten minutes). `--no-argo` skips the ArgoCD check.
877
877
 
878
+ ### skill
879
+
880
+ The CLI ships with a Claude skill: instructions that teach Claude (Claude Code
881
+ or any Agent Skills host) to drive these commands non-interactively, including
882
+ the exit codes, the draft/review/deploy model and a command reference.
883
+
884
+ ```
885
+ devlift skill install Install it for your user (~/.claude/skills/devlift).
886
+ devlift skill install --project Install it into this repository (./.claude/skills/devlift).
887
+ devlift skill install --force Replace an existing installation; do this after upgrading.
888
+ devlift skill status Where it is installed, and whether it matches this CLI.
889
+ devlift skill uninstall Remove it.
890
+ ```
891
+
892
+ Start a new Claude session after installing. Installing copies the files
893
+ rather than linking them, because an upgrade replaces the environment the CLI
894
+ lives in; `status` tells you when the copy has fallen behind, and
895
+ `install --force` refreshes it.
896
+
897
+ Installed for your user, the skill applies to every project on the machine.
898
+ Installed into a repository and committed, everyone who clones it gets the
899
+ skill without running anything.
900
+
878
901
  ### manual
879
902
 
880
903
  ```
@@ -1,3 +1,3 @@
1
1
  """DevLift command-line tool."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.2"
@@ -29,6 +29,7 @@ from devlift_cli.commands import manual as manual_cmds
29
29
  from devlift_cli.commands import repositories as repositories_cmds
30
30
  from devlift_cli.commands import request as request_cmds
31
31
  from devlift_cli.commands import s3 as s3_cmds
32
+ from devlift_cli.commands import skill as skill_cmds
32
33
  from devlift_cli.commands import sqs as sqs_cmds
33
34
  from devlift_cli.config import load_profile
34
35
  from devlift_cli.context import Invocation
@@ -82,6 +83,7 @@ app.command("logout")(auth_cmds.logout)
82
83
  app.command("whoami")(auth_cmds.whoami)
83
84
  app.command("configure")(auth_cmds.configure)
84
85
  app.add_typer(auth_cmds.profile_app, name="profile")
86
+ app.add_typer(skill_cmds.skill_app, name="skill")
85
87
  app.command("manual")(manual_cmds.manual)
86
88
  # `devlift man` — the name every terminal user reaches for first. Hidden from
87
89
  # the command list so the manual appears once there, under its full name.
@@ -0,0 +1,158 @@
1
+ """devlift skill install — put the bundled Claude skill where Claude looks.
2
+
3
+ The skill teaches Claude (Claude Code, or any Agent Skills host) to drive this
4
+ CLI: the rules for calling it non-interactively, the intent-to-command map, the
5
+ draft/review/deploy model, and a command reference.
6
+
7
+ It ships inside the wheel rather than living only in the source repository,
8
+ because the repository is private and the package is not. Someone who installed
9
+ with `pipx install devlift-cli` can run this and have Claude know the commands;
10
+ before, that needed a checkout nobody outside the team has.
11
+
12
+ Installing COPIES the files rather than linking them. A pipx upgrade replaces
13
+ the whole virtual environment, so a link into it is a link into something that
14
+ gets rebuilt; a copy keeps working and `devlift skill install --force` after an
15
+ upgrade refreshes it.
16
+ """
17
+
18
+ from __future__ import annotations
19
+
20
+ import shutil
21
+ from importlib import resources
22
+ from pathlib import Path
23
+
24
+ import typer
25
+
26
+ from devlift_cli import __version__
27
+ from devlift_cli.context import Invocation
28
+ from devlift_cli.errors import CliError
29
+ from devlift_cli.render import output
30
+ from devlift_cli.render.output import kv_table
31
+
32
+ skill_app = typer.Typer(help="The Claude skill for driving this CLI.", no_args_is_help=True)
33
+
34
+ SKILL_NAME = "devlift"
35
+ # Every file the skill is made of. Listed rather than globbed so a missing one
36
+ # is an error here instead of a skill that half-works inside Claude.
37
+ SKILL_FILES = (
38
+ "SKILL.md",
39
+ "reference/commands.md",
40
+ "reference/exit-codes.md",
41
+ "reference/workflows.md",
42
+ )
43
+
44
+
45
+ def _personal_dir() -> Path:
46
+ return Path.home() / ".claude" / "skills" / SKILL_NAME
47
+
48
+
49
+ def _project_dir() -> Path:
50
+ return Path.cwd() / ".claude" / "skills" / SKILL_NAME
51
+
52
+
53
+ def _source_files() -> list[tuple[str, str]]:
54
+ """(relative path, contents) for the packaged skill."""
55
+ root = resources.files("devlift_cli").joinpath("skill")
56
+ out = []
57
+ for name in SKILL_FILES:
58
+ try:
59
+ out.append((name, root.joinpath(name).read_text(encoding="utf-8")))
60
+ except (FileNotFoundError, OSError) as exc:
61
+ raise CliError(
62
+ f"The installed package is missing its skill file '{name}'.",
63
+ hint="Reinstall devlift-cli, or report this as a packaging bug.",
64
+ ) from exc
65
+ return out
66
+
67
+
68
+ @skill_app.command("install")
69
+ def skill_install(
70
+ ctx: typer.Context,
71
+ project: bool = typer.Option(False, "--project", help="Install into ./.claude/skills instead of your home directory."),
72
+ path: str | None = typer.Option(None, "--path", help="Install into this directory instead."),
73
+ force: bool = typer.Option(False, "--force", help="Overwrite an existing installation."),
74
+ ):
75
+ """Install the Claude skill so Claude can drive this CLI.
76
+
77
+ Personal by default (~/.claude/skills/devlift), which covers every project
78
+ on this machine. `--project` installs into the current repository instead,
79
+ where committing it gives the whole team the skill on clone.
80
+
81
+ Re-run with --force after upgrading devlift-cli to refresh the copy.
82
+ """
83
+ inv: Invocation = ctx.obj
84
+ target = Path(path).expanduser() if path else (_project_dir() if project else _personal_dir())
85
+
86
+ files = _source_files()
87
+
88
+ if target.exists() and not force:
89
+ # A symlink here is almost always an older install pointing into a
90
+ # source checkout, and silently replacing it would be the wrong call.
91
+ kind = "symlink" if target.is_symlink() else "directory"
92
+ raise CliError(
93
+ f"A skill is already installed at {target} ({kind}).",
94
+ hint="Pass --force to replace it.",
95
+ code=7,
96
+ )
97
+
98
+ if target.is_symlink():
99
+ target.unlink()
100
+ elif target.exists():
101
+ shutil.rmtree(target)
102
+
103
+ for name, text in files:
104
+ destination = target / name
105
+ destination.parent.mkdir(parents=True, exist_ok=True)
106
+ destination.write_text(text, encoding="utf-8")
107
+
108
+ output.success(f"Claude skill '{SKILL_NAME}' installed to {target} (from devlift-cli {__version__}).")
109
+ output.info("Start a new Claude session to pick it up. Re-run with --force after upgrading devlift-cli.")
110
+ inv.emit(
111
+ {"name": SKILL_NAME, "path": str(target), "files": [n for n, _ in files], "version": __version__},
112
+ lambda d: kv_table(
113
+ [("Skill", d["name"]), ("Installed to", d["path"]), ("From", f"devlift-cli {d['version']}"), ("Files", str(len(d["files"])))]
114
+ ),
115
+ )
116
+
117
+
118
+ @skill_app.command("uninstall")
119
+ def skill_uninstall(
120
+ ctx: typer.Context,
121
+ project: bool = typer.Option(False, "--project", help="Remove from ./.claude/skills instead of your home directory."),
122
+ path: str | None = typer.Option(None, "--path", help="Remove from this directory instead."),
123
+ ):
124
+ """Remove an installed Claude skill."""
125
+ target = Path(path).expanduser() if path else (_project_dir() if project else _personal_dir())
126
+ if not target.exists() and not target.is_symlink():
127
+ output.info(f"Nothing installed at {target}.")
128
+ return
129
+ if target.is_symlink():
130
+ target.unlink()
131
+ else:
132
+ shutil.rmtree(target)
133
+ output.success(f"Removed {target}.")
134
+
135
+
136
+ @skill_app.command("status")
137
+ def skill_status(ctx: typer.Context):
138
+ """Show where the skill is installed, and whether it matches this CLI."""
139
+ inv: Invocation = ctx.obj
140
+ packaged = dict(_source_files())
141
+
142
+ rows = []
143
+ for label, target in (("personal", _personal_dir()), ("project", _project_dir())):
144
+ if target.is_symlink():
145
+ state = f"symlink -> {target.resolve()}"
146
+ elif not target.exists():
147
+ state = "not installed"
148
+ else:
149
+ try:
150
+ same = all(
151
+ (target / n).read_text(encoding="utf-8") == text for n, text in packaged.items()
152
+ )
153
+ except OSError:
154
+ same = False
155
+ state = "up to date" if same else "differs from this CLI — run: devlift skill install --force"
156
+ rows.append({"scope": label, "path": str(target), "state": state})
157
+
158
+ inv.emit(rows, lambda d: kv_table([(r["scope"], f"{r['state']}\n{r['path']}") for r in d], title=f"Claude skill (devlift-cli {__version__})"))
@@ -0,0 +1,192 @@
1
+ ---
2
+ name: devlift
3
+ description: Operate the DevLift platform from the terminal with the `devlift` CLI — create S3 buckets, SQS queues, DynamoDB tables and EKS services, edit service settings, add Kong routes, run the review lane (submit, approve, reject), deploy approved changes and check deployment and ArgoCD status. Use whenever a user asks to provision, configure, ship, review or check anything on DevLift and a shell is available; never fall back to aws/kubectl/terraform.
4
+ ---
5
+
6
+ # DevLift CLI
7
+
8
+ `devlift` (alias `dl`) is a deterministic command-line client for the DevLift
9
+ backend: every command is a fixed sequence of REST calls, the same ones the
10
+ web dashboard makes. It is not an assistant and holds no state between calls.
11
+ Full reference: `devlift manual` (or `reference/commands.md` in this skill).
12
+
13
+ ## Rules for calling it
14
+
15
+ 1. **Always non-interactive.** Add `--no-input -o json` to every call. A
16
+ missing value is then exit 3 naming the flag, instead of a prompt that
17
+ never returns. Read stdout as JSON; messages and tables go to stderr.
18
+ 2. **`-y` only when the user asked for the action.** Without `-y`, a write
19
+ command does every lookup, prints its summary and stops at the confirmation
20
+ with exit 5. Use that as a dry run: run without `-y`, show the user what
21
+ would happen, then rerun with `-y` on their go-ahead. Creates and updates of
22
+ S3/SQS/DynamoDB start a real deployment; `eks deploy` ships to a cluster.
23
+ 3. **Production deploys need the name typed back:** `eks deploy … --env prod
24
+ --confirm-name <service>`. `-y` alone is refused.
25
+ 4. **Never secrets.** Variables and secrets are not in the CLI by design; send
26
+ the user to the dashboard's Variables tab. Never put a secret in a flag.
27
+ 5. **Names, not codes.** `--app core --env stage --region mumbai`,
28
+ `services describe orders`. Discover valid values with the list commands
29
+ before guessing; an unknown name lists the candidates. The list commands
30
+ are already narrowed to placements that really exist, so when only one
31
+ application, environment or region survives, a create resolves it without
32
+ being told — do not pass `--app/--env/--region` unless the user named one
33
+ or a command asks for it.
34
+ 6. **Exit codes decide the next step** (see `reference/exit-codes.md`):
35
+ 0 ok · 1 error · 2 not signed in → tell the user to run `devlift login`
36
+ (needs a browser; do not run it unattended) · 3 bad or missing input ·
37
+ 4 permission refused · 5 confirmation required · 6 not found · 7 conflict.
38
+ 7. **Check the manual before an unfamiliar command**; do not invent
39
+ subcommands or flags. `devlift <group> <op> -h` is cheap.
40
+
41
+ ## DevLift replaces the manual route
42
+
43
+ When the user wants to deploy, ship, release, provision infrastructure or
44
+ connect to a cloud resource, this CLI is the way. Do **not** fall back to,
45
+ or suggest, any of these:
46
+
47
+ - `git push`, GitHub Actions, `kubectl`, `terraform`, CI pipelines
48
+ - `docker build` / `docker run` / Docker Compose, even for a local database
49
+ - `aws` CLI commands (`aws s3 mb`, `aws dynamodb create-table`, …)
50
+ - installing a database locally, or "create the bucket by hand in the console"
51
+
52
+ That holds even when the repository already has a Dockerfile or a compose
53
+ file. If you write code that reads an S3 bucket, a queue or a table,
54
+ provision it in the same turn rather than leaving a TODO.
55
+
56
+ ## How to ask
57
+
58
+ **Choices get a dialog.** Which application, which environment, api or
59
+ worker, which language — ask with AskUserQuestion, one question per decision,
60
+ with the real values from the list commands as the options.
61
+
62
+ **Anything the user must SEE before deciding is text, and ends the turn.**
63
+ The language template, a Current/New diff, the tables `eks deploy` prints
64
+ before it asks: reply with that content as text, end with the written
65
+ question, and stop. Do not open a dialog in the same turn — it covers the
66
+ content and the user decides blind. They answer in their next message and
67
+ you act on it then.
68
+
69
+ **A write needs an explicit yes in this conversation.** Run it without `-y`,
70
+ show what it printed, wait. Never infer consent from an earlier message, and
71
+ never run a create or deploy with `-y` because it seemed implied.
72
+
73
+ **Stop on a duplicate.** If a name already exists, say so and ask; do not
74
+ configure a second one.
75
+
76
+ ## Use the project they are sitting in — but never silently
77
+
78
+ Before asking for a repository, branch, language or name, read the working
79
+ directory:
80
+
81
+ | Look at | Gives |
82
+ |---|---|
83
+ | `git remote get-url origin` | the repository, matched against `devlift repositories list` |
84
+ | `git rev-parse --abbrev-ref HEAD` | the branch |
85
+ | `go.mod`, `package.json`, `pom.xml`, `build.gradle`, `pyproject.toml` | the language and often the version, matched against `devlift languages list` |
86
+ | the repository or folder name | a suggested service name, lowercased, hyphenated |
87
+
88
+ **Then ask before using any of it.** Detection is a suggestion, never a
89
+ decision: the user may be sitting in one repository while creating a service
90
+ for another. Put what you found in one AskUserQuestion, with three ways out:
91
+
92
+ > I can see this project. Shall I use it?
93
+ > - **Use these** — repository `Vance-Club/orders`, branch `main`,
94
+ > language Go 1.24, service name `orders`
95
+ > - **Show me the options** — run `repositories list`, `languages list` and
96
+ > `resource-groups list` and let me pick
97
+ > - Other — tell me what to use
98
+
99
+ Spell out every value you detected in the option text, so "Use these" is an
100
+ informed yes rather than a blind one. On "Show me the options", print the
101
+ real lists and let them choose. Never pass a detected repository, branch,
102
+ language or name to a command the user has not seen and agreed to.
103
+
104
+ The resource group is never detectable, so it is always asked. Say what it
105
+ decides when you ask: it is the permission boundary, so it determines who
106
+ can later edit, approve and deploy the service.
107
+
108
+ ## What maps to what
109
+
110
+ | User wants | Command |
111
+ |---|---|
112
+ | a bucket / queue / table | `devlift s3|sqs|dynamodb create --name … --app … --env … --region …` |
113
+ | change a bucket or queue setting | `devlift s3|sqs update <name> --env … <flags>` |
114
+ | a new service | `devlift eks create --name … --app … --env … --region … --type api|worker --repo owner/name --branch main --language Go --version 1.24 [--set k=v]` |
115
+ | change a service's settings | `devlift eks edit <service> --env … --set k=v` (saves a draft) |
116
+ | see a service's config | `devlift eks show <service>`, `devlift eks settings <service>` |
117
+ | preview what a pending request changes | `devlift eks diff <service>` |
118
+ | add / remove / rename a Kong route | `devlift kong route add|remove|rename <service> --env … --method … …` |
119
+ | send a change for review | `devlift request submit <service>` |
120
+ | review a change | `devlift approval list`, `approval approve|request-changes|reject|revoke <service>` |
121
+ | ship an approved change | `devlift eks deploy <service> --env … [--wait]` |
122
+ | is the deploy done / is the app up | `devlift eks status <service> --env …`, `devlift deployment status <workflow-id> --wait` |
123
+ | what exists | `applications|environments|regions|resource-groups|services|repositories|languages|clusters list`, `repositories branches owner/name` |
124
+
125
+ Recipes with the exact sequence of calls and what to read from each result
126
+ are in `reference/workflows.md`.
127
+
128
+ ## The model behind the commands
129
+
130
+ - **Resources (S3, SQS, DynamoDB)** deploy directly: create/update registers
131
+ the row, queues it, approves it and starts the deployment in one command.
132
+ `--wait` follows the workflow; otherwise the result carries `workflow_id`
133
+ for `deployment status`.
134
+ - **Services and Kong routes** never deploy directly. `eks create`, `eks edit`
135
+ and `kong route …` each save a **draft** queue row. A service's settings
136
+ row and gateway row form one **change set** and move together through the
137
+ review lane: `request submit` → `approval approve` → `eks deploy`. Naming a
138
+ service acts on the whole set; the output lists every row that moved.
139
+ - **Templates fill settings.** `eks create` needs only what no template can
140
+ know (repository, branches, language, version). Port, paths, CPU, memory,
141
+ replicas and the rest come from the platform's per-language template and
142
+ are printed with a "From" column before the confirmation; `--set key=value`
143
+ overrides any of them, `devlift eks settings` lists the keys and when each
144
+ applies (JVM heap only for Java, Go config only for Go, health/service path
145
+ only for API services, min/max replicas only with autoscaling).
146
+ - **Two status questions.** `deployment status` says whether DevLift's
147
+ pipeline finished; `eks status` also asks ArgoCD whether the pod is serving,
148
+ and reports "waiting for ArgoCD" when Argo has not acted since the deploy.
149
+
150
+ ## Creating a service: the conversation
151
+
152
+ 1. **Collect what only the user knows.** Detect what you can from the
153
+ project and offer it as above, then ask for the rest: service name, type
154
+ (api or worker) and resource group. Application, environment and region
155
+ usually resolve themselves, so do not ask unless a command asks you.
156
+ Offer real values from `repositories list`, `repositories branches`,
157
+ `languages list` and `resource-groups list` rather than guessing; a wrong
158
+ name costs a round trip and exit 6.
159
+ 2. **Dry-run** `eks create … --no-input -o json` without `-y`. It exits 5
160
+ and prints two tables on stderr: the service (placement, cluster it
161
+ picked, repository, branches, language) and the Configuration table with
162
+ a "From" column saying which values came from the language template.
163
+ 3. **Show the user those template values as they were printed and ask:**
164
+ "These are the standard <language> values. Apply them, or tell me what to
165
+ change?" That reply is text and ends the turn — no dialog, no tool call,
166
+ because a dialog would hide the table they are deciding on. Do not rerun
167
+ with `-y` until they answer.
168
+ 4. A change ("port 9000", "1 GiB memory limit", "turn on autoscaling 2 to 4")
169
+ becomes `--set` flags on the rerun: `--set port=9000`,
170
+ `--set memory_limit=1`, `--set hpa_enabled=true --set min_replicas=2
171
+ --set max_replicas=4`. Rerun without `-y` once more only if the change is
172
+ large; otherwise rerun with `-y` and report the service, configuration and
173
+ queue codes.
174
+ 5. Say it is a **draft** and name the next step: `request submit`, then a
175
+ reviewer's `approval approve`, then `eks deploy`.
176
+
177
+ The same show-then-ask shape applies to every write: `eks edit` and the
178
+ update commands print Current | New, `kong route …` prints the route change,
179
+ the review verbs print the request with its frozen diff, `eks deploy` lists
180
+ the approved rows. The summary is printed on every dry run, in json mode too.
181
+
182
+ ## Reporting back to the user
183
+
184
+ - After a dry run (exit 5) show the summary the command printed and ask for
185
+ the go-ahead. After a write, report the codes it returned: resource code and
186
+ workflow id for resources; queue code and status for drafts and review
187
+ verbs; workflow id for a deploy.
188
+ - A draft is not deployed. Say so, and name the next command
189
+ (`request submit`, then `approval approve`, then `eks deploy`).
190
+ - On exit 7 quote the message: it names the conflicting request or the rule
191
+ (one live request per service, one auth per Kong tag, a name that exists).
192
+ - On exit 2 stop and tell the user to sign in; on exit 4 say who to ask.
@@ -0,0 +1,162 @@
1
+ # Command reference (condensed from `devlift manual`)
2
+
3
+ Global options work anywhere on the line: `--profile NAME`, `-o json|table|yaml`,
4
+ `-y/--yes`, `--no-input`, `--endpoint-url URL`, `--debug`, `-h`. Environment:
5
+ `DEVLIFT_PROFILE`, `DEVLIFT_BASE_URL`, `DEVLIFT_TOKEN` (CI token, used as-is),
6
+ `DEVLIFT_OUTPUT`, `DEVLIFT_NO_CACHE=1`.
7
+
8
+ ## Identity and discovery
9
+
10
+ ```
11
+ devlift whoami user, tenant, backend, enabled features
12
+ devlift profile list profiles; the default is starred
13
+ devlift profile show which profile this shell uses, and why
14
+ devlift profile use NAME switch the default profile (lasts until changed)
15
+ devlift applications list [--active]
16
+ devlift environments list [--app NAME] environments and their regions per application
17
+ devlift regions list [--app NAME] [--env ENV] the --region values
18
+ devlift resource-groups list [--app NAME]
19
+ devlift resource-types list [--family FAMILY]
20
+ devlift services list [--app NAME] [--search TEXT]
21
+ devlift services describe <name|code>
22
+ devlift repositories list [--search TEXT] the --repository values (GitHub App visibility)
23
+ devlift repositories branches <owner/name> the --branch values
24
+ devlift languages list the --language / --version values, template yes/no
25
+ devlift eks settings the --set keys: values, when each applies, required
26
+ ```
27
+
28
+ Catalog answers are cached ten minutes per profile; `--no-cache` on a list
29
+ command or `DEVLIFT_NO_CACHE=1` refetches.
30
+
31
+ ## Resources (deploy directly)
32
+
33
+ ```
34
+ devlift s3 list [--app] [--env] [--region] devlift s3 describe <bucket|code> [--env]
35
+ devlift s3 create --name NAME --app APP --env ENV --region REGION
36
+ [--versioning|--no-versioning] [--replication --cross-account-id 123456789012|--no-replication] [--wait] [-y]
37
+ devlift s3 update <bucket|code> [--env] [--versioning|--no-versioning] [--replication --cross-account-id ID|--no-replication] [--wait] [-y]
38
+
39
+ devlift sqs list / describe (same shape)
40
+ devlift sqs create --name NAME --app APP --env ENV --region REGION
41
+ [--fifo|--no-fifo] [--dlq|--no-dlq] [--cross-account-id ID ...]
42
+ [--max-receive-count 1-1000] [--visibility-timeout 0-43200] [--retention 60-1209600] [--dlq-retention 60-1209600] [--wait] [-y]
43
+ devlift sqs update <queue|code> [--env] [--dlq|--no-dlq] [--cross-account-id ID ...|--clear-cross-accounts]
44
+ [--max-receive-count N] [--visibility-timeout N] [--retention N] [--dlq-retention N] [--wait] [-y]
45
+
46
+ devlift dynamodb list / describe
47
+ devlift dynamodb create --name NAME --app APP --env ENV --region REGION --partition-key ATTR [--partition-key-type S|N|B] [--wait] [-y]
48
+ ```
49
+
50
+ Sequence behind create/update: duplicate check → `POST /infrastructures`
51
+ (upsert with `code` on update) → add to queue → approve → deploy → optional
52
+ wait. Result JSON: `resource_code`, `queue_code`, `status`, `workflow_id` or
53
+ `pr_url`. Update prints Field | Current | New and sends the full merged
54
+ config; name, application, environment, region (and FIFO for SQS) cannot
55
+ change. Bucket names 3-63 lowercase/digits/dots/hyphens; queue names ≤80
56
+ chars without `.fifo`; table names 3-200 chars, immutable after creation.
57
+
58
+ ## EKS services (save drafts)
59
+
60
+ ```
61
+ devlift eks create --name NAME --app APP --env ENV --region REGION --type api|worker
62
+ [--resource-group GROUP] [--cluster CLUSTER]
63
+ --repository owner/name --branch main [--branch ...]
64
+ --language Go|Python|Node.js|"Java Maven"|"Java Gradle" --version 1.24
65
+ [--set key=value ...] [--public] [-y]
66
+ devlift eks show <service> --env ENV configuration as it stands; pending values marked
67
+ devlift eks diff <service> --env ENV preview the pending request: Field | Deployed | Requested
68
+ devlift eks diff <service> --deployed instead: live config vs the last deployment
69
+ devlift eks settings <service> --env ENV the --set keys that apply to it, with current values
70
+ devlift clusters list [--type eks] [--app] [--env] the --cluster values (Registered = may take a service)
71
+ devlift eks edit <service> --env ENV [--set key=value ...] [--repository owner/name] [--branch ...] [--language L --version V] [-y]
72
+ devlift eks deploy <service> --env ENV [--region R] [--confirm-name NAME] [--wait] [-y]
73
+ devlift eks status <service> --env ENV [--wait] [--no-argo]
74
+ ```
75
+
76
+ - create = `POST /services/create-service` → `POST /service-configs`
77
+ (placement only) → `POST /transaction/service-settings/<config>` (DRAFT).
78
+ Result JSON: `service_code`, `service_config_code`, `queue_code`,
79
+ `queue_status`, `config`.
80
+ - Service name: lowercase letters, digits, hyphens. Repository must be visible
81
+ to the GitHub App (bare name accepted when unique); each branch must exist.
82
+ - Several settings go in one `--set` separated by semicolons
83
+ (`--set "cpu_limit=0.5;port=9090"`, quoted), or repeat the flag. A comma
84
+ separates values INSIDE one setting, so the two nest:
85
+ `--set "custom_iam_policies=s3,sqs;port=9090"`.
86
+ - `--set` keys (booleans `true|false`, lists comma-separated, build args
87
+ `NAME=VALUE,…`): port, health, service_path, build_path, dockerfile_path,
88
+ go_config_path, xms, xmx, alb_schema (internal|internet-facing), compute
89
+ (on-demand|spot), auth_mode (pod_identity|irsa), cpu_requested, cpu_limit,
90
+ memory_requested, memory_limit (cores / GiB; 0.1-4 / 0.1-8; limit ≥ request),
91
+ replica_count, hpa_enabled, min_replicas, max_replicas (1-100, max > min),
92
+ generate_dockerfile, go_use_aws_secrets, create_ecr, create_secrets,
93
+ create_ssm, create_argo, other_paths, custom_iam_policies (s3,sqs,dynamodb,ses),
94
+ build_args.
95
+ - Conditional: xms/xmx Java only; go_config_path/go_use_aws_secrets Go only;
96
+ build_path Java+Go; dockerfile_path only with generate_dockerfile=false;
97
+ alb_schema/health/service_path API only (required there); min/max with
98
+ hpa_enabled=true; replica_count with hpa_enabled=false. A `--set` that does
99
+ not apply is refused (exit 3); a template value that does not apply falls away.
100
+ - edit: live config + your open draft → Current | New → settings draft,
101
+ reusing the open draft's queue code. Blocked (exit 7) while a submitted or
102
+ approved request exists on the service.
103
+ - deploy = `POST /deployments/multiple-deploy {service_config_code}`: the
104
+ backend resolves the approved rows, re-checks the seal, starts one workflow.
105
+ Result JSON: `workflow_id`, `requests[]`. Prod: `--confirm-name`.
106
+ - status: latest deployment for the configuration (stages, PR) plus ArgoCD
107
+ `state` (up | starting | unhealthy | waiting_for_argocd | not_picked_up |
108
+ missing | suspended | not_found | not_configured | unavailable), health,
109
+ sync, running image, URLs. `settled: false` means polling would change it.
110
+
111
+ ## Kong routes (save drafts)
112
+
113
+ ```
114
+ devlift kong routes list <service> [--env ENV] [--region R]
115
+ devlift kong route add <service> --env ENV --method GET|POST|PUT|PATCH|DELETE|OPTIONS --auth jwt|public
116
+ --path '~/api/v1/orders$' [--path ...] [--tag TAG] [--priority 0-10000] [--plugin "User ID Injection"] [-y]
117
+ devlift kong route remove <service> --env ENV --method M --tag TAG --path '~/...$' [--path ...] [-y]
118
+ devlift kong route rename <service> --env ENV --method M --tag TAG --from '~/old$' --to '~/new$' [-y]
119
+ devlift kong plugin add <service> --env ENV --method M --tag TAG --plugin "User ID Injection" [--priority N] [-y]
120
+ ```
121
+
122
+ One change = one route group (tag × method). Default tag: the service name
123
+ for JWT, `<service>-open` for public. A tag has one auth; mixing is exit 7.
124
+ Paths: `~/…$`, no spaces, params as regex groups `(?<id>[^/]+)`, never `/:id`.
125
+ Already-present paths are skipped; removing/renaming an absent path is exit 6.
126
+ Write = `POST /transaction/kong-gateway/<config>` (DRAFT, gateway half of the
127
+ service's change set). Result JSON: `changed`, `change{}`, `queue_code`.
128
+
129
+ ## Review lane
130
+
131
+ ```
132
+ devlift request list [--status draft|submit|approved|rejected] [--service NAME --env ENV] [--mine]
133
+ devlift request show <queue-code|service> [--env ENV] frozen diff (Field | From | To)
134
+ devlift request history <service> [--env ENV]
135
+ devlift request submit|withdraw|discard <queue-code|service> [--env ENV] [--comment TEXT] [-y]
136
+ devlift approval list reviewer inbox by service
137
+ devlift approval approve|revoke <queue-code|service> [--env ENV] [--comment TEXT] [-y]
138
+ devlift approval request-changes|reject <queue-code|service> [--env ENV] --comment TEXT [-y]
139
+ ```
140
+
141
+ States: draft →submit→ submit →approve→ approved →deploy→ in flight.
142
+ withdraw (submitter) and request-changes (reviewer, comment) return to draft;
143
+ reject (reviewer, comment) is terminal; revoke returns approved to submit
144
+ while nothing is deploying; discard bins a draft. Every verb moves the whole
145
+ change set (settings + gateway rows of one author); one live request per
146
+ service at a time. Result JSON: `requests[] {code, kind, status}`.
147
+
148
+ ## Deployments
149
+
150
+ ```
151
+ devlift deployment list [--status running|completed|failed|timedout] [--app NAME] [--env ENV] [--mine] [--limit N]
152
+ devlift deployment status <workflow-id> [--wait] status, PR, resources, pipeline stages
153
+ devlift queue list [--env ENV] your transaction-queue items
154
+ ```
155
+
156
+ Terminal statuses: COMPLETED, FAILED, TIMEOUT, CANCELLED. Stage rows carry
157
+ name, status, started_at, ended_at, error.
158
+
159
+ ## Not in the CLI
160
+
161
+ Variables and secrets (dashboard Variables tab only, by design), `data
162
+ query`, interactive shell.
@@ -0,0 +1,16 @@
1
+ # Exit codes and what to do
2
+
3
+ | Code | Meaning | What to do |
4
+ |---|---|---|
5
+ | 0 | Success | Read stdout JSON. A write returns codes (resource, queue item, workflow). `changed: false` means nothing needed doing. |
6
+ | 1 | Error | Server-side or unexpected failure. Quote `error:` from stderr. Retry once only if it reads as transient (status service busy). |
7
+ | 2 | Not signed in, or the token belongs to another backend | Stop. Tell the user to run `devlift login` in their terminal (opens a browser). If stderr says "signed in to X, but this command targets Y", the shell exports `DEVLIFT_BASE_URL`; unset it or log in to that backend. |
8
+ | 3 | Missing or invalid input | The message names the flag or key. Fix and rerun. Includes an ambiguous name (candidates are listed), a `--set` that does not apply to this language/service type, a value out of range, a missing `--comment` on reject/request-changes. |
9
+ | 4 | Permission refused by the server | The user lacks the OpenFGA relation (can_write_settings, can_approve, can_deploy…). Say so; do not retry. |
10
+ | 5 | Confirmation required | Expected on every write without `-y` under `--no-input`. Show the printed summary, get the user's yes, rerun with `-y`. |
11
+ | 6 | Not found | Unknown service, bucket, queue code, repository, branch, cluster, or no request in the state the verb needs ("No draft request of yours on X"). |
12
+ | 7 | Conflict | Name already exists; request in the wrong state ("is approved — only submitted can be…"); another author's request holds the service's review lane; Kong tag already has the other auth; deployment already in progress. Quote the message, it says what to do. |
13
+
14
+ Stderr format is always `error: <one sentence>` followed by an optional hint
15
+ line. With `-o json`, stdout carries JSON only, so it is safe to parse even
16
+ when stderr has messages.
@@ -0,0 +1,105 @@
1
+ # Workflows: exact call sequences
2
+
3
+ Every command below is run as `devlift … --no-input -o json`; `-y` is added
4
+ only after the user has seen the dry run and agreed. `jq` expressions show
5
+ what to read from each result.
6
+
7
+ ## 0. Preflight (once per session)
8
+
9
+ ```
10
+ devlift whoami --no-input -o json # exit 2 → user must `devlift login`
11
+ devlift applications list --no-input -o json | jq -r '.[].application_name'
12
+ devlift environments list --app core --no-input -o json # environments and regions for that application
13
+ ```
14
+
15
+ ## 1. Create a bucket / queue / table
16
+
17
+ ```
18
+ devlift s3 create --name orders-archive --app core --env stage --region mumbai --versioning --no-replication --no-input # exit 5: summary printed
19
+ devlift s3 create --name orders-archive --app core --env stage --region mumbai --versioning --no-replication --no-input -y -o json
20
+ # → {resource_code, queue_code, status, workflow_id | pr_url}
21
+ devlift deployment status <workflow_id> --wait --no-input -o json
22
+ ```
23
+
24
+ SQS: `--fifo|--no-fifo --dlq|--no-dlq` are required decisions; pass them.
25
+ DynamoDB: `--partition-key ATTR [--partition-key-type S|N|B]`.
26
+ A duplicate name exits 7; `s3 describe <name> --env stage` shows the existing one.
27
+
28
+ ## 2. Change a bucket or queue
29
+
30
+ ```
31
+ devlift sqs update orders --env stage --dlq --max-receive-count 5 --no-input # Field | Current | New, exit 5
32
+ devlift sqs update orders --env stage --dlq --max-receive-count 5 --no-input -y -o json
33
+ ```
34
+ `changed: false` with exit 0 means the values were already set. Same
35
+ sequence as create afterwards (queue → approve → deploy).
36
+
37
+ ## 3. Create an EKS service
38
+
39
+ ```
40
+ devlift repositories list --no-input -o json | jq -r '.[].full_name'
41
+ devlift repositories branches Regobs/orders --no-input -o json | jq -r '.[].name'
42
+ devlift languages list --no-input -o json
43
+ devlift eks create --name orders --app core --env stage --region mumbai --type api \
44
+ --repo Regobs/orders --branch main --language go --version 1.24 --no-input -o json 2>&1 # exit 5: two tables (service, configuration with From column) on stderr
45
+ # SHOW the Configuration table to the user and ASK: "These are the standard Go values. Apply them, or tell me what to change?"
46
+ # a change becomes --set on the rerun, e.g. --set cpu_limit=0.5 --set hpa_enabled=true --set min_replicas=1 --set max_replicas=3
47
+ devlift eks create … --no-input -y -o json
48
+ # → {service_code, service_config_code, queue_code, queue_status: "draft", config}
49
+ ```
50
+ Then tell the user it is a draft and continue with workflow 6.
51
+ `--type worker` drops health/service_path/ALB fields (no load balancer).
52
+
53
+ ## 4. Change a running service's settings
54
+
55
+ ```
56
+ devlift eks show orders --env stage --no-input -o json # current values; pending_request + pending_keys if a draft exists
57
+ devlift eks edit orders --env stage --set memory_limit=1 --no-input # Current | New, exit 5
58
+ devlift eks edit orders --env stage --set memory_limit=1 --no-input -y -o json
59
+ # → {changed, changes{field:{from,to}}, queue_code, queue_status}
60
+ ```
61
+ Exit 7 "has a submit/approved settings request": the change set is under
62
+ review; use `request withdraw` (own) or wait for deploy/revoke.
63
+
64
+ ## 5. Kong routes
65
+
66
+ ```
67
+ devlift kong routes list orders --env stage --no-input -o json # groups: method, auth, tag, paths, pending
68
+ devlift kong route add orders --env stage --method GET --auth jwt --path '~/orders/api/v1/orders$' --no-input # exit 5
69
+ devlift kong route add … --no-input -y -o json # → {changed, change{tag,method,added,…}, queue_code}
70
+ ```
71
+ Public routes: `--auth public` (default tag `orders-open`). Existing tag with
72
+ the other auth → exit 7: pick another `--tag`.
73
+
74
+ ## 6. Review lane (settings and routes move together)
75
+
76
+ ```
77
+ devlift request list --service orders --env stage --no-input -o json # what exists, statuses, you.can_approve / can_deploy
78
+ devlift request submit orders --no-input -y -o json # → requests[] all "submit"; exit 7 if another author holds the lane
79
+ devlift approval list --no-input -o json # reviewer inbox
80
+ devlift request show orders --no-input -o json # frozen diff for the reviewer
81
+ devlift approval approve orders --no-input -y -o json # → requests[] all "approved"
82
+ # alternatives: approval request-changes orders --comment "…" -y ; approval reject orders --comment "…" -y ; approval revoke orders -y ; request withdraw orders -y
83
+ ```
84
+ Self-approval works only where the resource group's rule allows it (403 → exit 4 otherwise).
85
+
86
+ ## 7. Deploy and verify
87
+
88
+ ```
89
+ devlift eks deploy orders --env stage --no-input # lists approved rows, exit 5
90
+ devlift eks deploy orders --env stage --no-input -y --wait -o json # → {workflow_id, status, state}
91
+ devlift eks status orders --env stage --no-input -o json # .deployment (stages, pr_url) and .application (ArgoCD state)
92
+ ```
93
+ Production: `--confirm-name orders` in place of relying on `-y`.
94
+ Read `.application.state`: `up` = healthy pod; `waiting_for_argocd` /
95
+ `starting` = keep polling (`--wait`); `not_configured` = no ArgoCD for that
96
+ environment, only the pipeline result is available.
97
+
98
+ ## 8. Find things
99
+
100
+ ```
101
+ devlift services list --search order --no-input -o json
102
+ devlift deployment list --env stage --status running --no-input -o json
103
+ devlift deployment status <workflow-id> --no-input -o json
104
+ devlift request history orders --env stage --no-input -o json
105
+ ```
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: devlift-cli
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: DevLift command-line tool: create, change, review and deploy DevLift resources from the terminal.
5
5
  License: Proprietary
6
6
  Keywords: devlift,devops,deployment,infrastructure,cli
@@ -40,17 +40,29 @@ the same endpoints the web dashboard uses. Nothing here talks to an LLM.
40
40
  ## Install
41
41
 
42
42
  ```
43
- pip install devlift-cli
43
+ pipx install devlift-cli
44
44
  ```
45
45
 
46
- Or, to keep it in its own environment and off your system Python:
46
+ pipx gives the tool its own environment and puts `devlift` and `dl` on your
47
+ PATH. If you do not have it:
47
48
 
48
49
  ```
49
- pipx install devlift-cli
50
+ sudo apt install pipx # Debian / Ubuntu
51
+ brew install pipx # macOS
52
+ pipx ensurepath # once, then reopen your terminal
50
53
  ```
51
54
 
52
- Upgrade with `pip install -U devlift-cli` (or `pipx upgrade devlift-cli`).
53
- Your profile and sign-in live in `~/.config/devlift` and survive upgrades.
55
+ Upgrade with `pipx upgrade devlift-cli`. Your profile and sign-in live in
56
+ `~/.config/devlift` and survive upgrades.
57
+
58
+ **Using pip instead?** Do it inside a virtual environment. A plain
59
+ `pip install devlift-cli` fails on recent Debian, Ubuntu and Fedora with
60
+ `externally-managed-environment`, because those systems refuse to let pip
61
+ touch the system Python.
62
+
63
+ ```
64
+ python3 -m venv ~/.venvs/devlift && ~/.venvs/devlift/bin/pip install devlift-cli
65
+ ```
54
66
 
55
67
  Then:
56
68
 
@@ -61,9 +73,9 @@ devlift whoami
61
73
  devlift man
62
74
  ```
63
75
 
64
- Requires Python 3.11 or newer. To remove it: `pip uninstall devlift-cli`
65
- (or `pipx uninstall devlift-cli`). That leaves `~/.config/devlift`, so delete
66
- that too if you want your profiles and sign-in gone as well.
76
+ Requires Python 3.11 or newer. To remove it: `pipx uninstall devlift-cli`.
77
+ That leaves `~/.config/devlift`, so delete that too if you want your profiles
78
+ and sign-in gone as well.
67
79
 
68
80
  Several backends? `devlift profile list`, `devlift profile use <name>` and
69
81
  `devlift profile show` switch between them and say which one is active.
@@ -92,14 +104,18 @@ Variables and secrets stay in the dashboard by design.
92
104
 
93
105
  ## Claude skill
94
106
 
95
- `skills/devlift/` teaches Claude (Claude Code or any Agent Skills host) to
96
- drive this CLI non-interactively. Install with:
107
+ A Claude skill ships inside the package, teaching Claude (Claude Code or any
108
+ Agent Skills host) to drive this CLI non-interactively. Install it with:
97
109
 
98
110
  ```
99
- ln -s "$(pwd)/skills/devlift" ~/.claude/skills/devlift # personal
100
- # or copy it into a project's .claude/skills/devlift
111
+ devlift skill install # ~/.claude/skills/devlift, every project
112
+ devlift skill install --project # ./.claude/skills/devlift, this repo only
101
113
  ```
102
114
 
115
+ Start a new Claude session afterwards to pick it up. `devlift skill status`
116
+ says where it is installed and whether it still matches the CLI;
117
+ `devlift skill install --force` refreshes it after an upgrade.
118
+
103
119
  ## Development
104
120
 
105
121
  Building, testing and cutting a release: see `CONTRIBUTING.md` in the source
@@ -42,6 +42,7 @@ devlift_cli/commands/manual.py
42
42
  devlift_cli/commands/repositories.py
43
43
  devlift_cli/commands/request.py
44
44
  devlift_cli/commands/s3.py
45
+ devlift_cli/commands/skill.py
45
46
  devlift_cli/commands/sqs.py
46
47
  devlift_cli/data/placement/vance.json
47
48
  devlift_cli/ops/__init__.py
@@ -56,4 +57,8 @@ devlift_cli/render/__init__.py
56
57
  devlift_cli/render/output.py
57
58
  devlift_cli/resolve/__init__.py
58
59
  devlift_cli/resolve/allowlist.py
59
- devlift_cli/resolve/names.py
60
+ devlift_cli/resolve/names.py
61
+ devlift_cli/skill/SKILL.md
62
+ devlift_cli/skill/reference/commands.md
63
+ devlift_cli/skill/reference/exit-codes.md
64
+ devlift_cli/skill/reference/workflows.md
@@ -51,4 +51,8 @@ version = { attr = "devlift_cli.__version__" }
51
51
  include = ["devlift_cli*"]
52
52
 
53
53
  [tool.setuptools.package-data]
54
- devlift_cli = ["MANUAL.md", "data/placement/*.json"]
54
+ # The skill ships inside the package on purpose: `devlift skill install` has to
55
+ # work for someone who installed from PyPI and has no access to this private
56
+ # repository, and shipping both in one wheel means the skill can never describe
57
+ # a different set of commands from the CLI that carried it.
58
+ devlift_cli = ["MANUAL.md", "skill/SKILL.md", "skill/reference/*.md", "data/placement/*.json"]
File without changes