@thejoseki/clawform 2.3.1 → 2.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +141 -120
- package/bin/clawform.js +161 -151
- package/package.json +65 -65
- package/src/aws/catalog.js +116 -116
- package/src/aws/changeset.js +231 -231
- package/src/aws/drift.js +59 -59
- package/src/aws/exports.js +213 -213
- package/src/aws/inventory.js +156 -156
- package/src/commands/activate.js +137 -105
- package/src/commands/ci-init.js +393 -212
- package/src/commands/cost-report.js +163 -163
- package/src/commands/deactivate.js +9 -2
- package/src/commands/deploy.js +413 -413
- package/src/commands/diff.js +39 -39
- package/src/commands/drift.js +35 -35
- package/src/commands/init-plugin.js +6 -6
- package/src/commands/init.js +360 -360
- package/src/commands/rollback.js +126 -126
- package/src/commands/status.js +147 -147
- package/src/commands/sync.js +50 -50
- package/src/commands/update.js +24 -0
- package/src/commands/validate.js +349 -349
- package/src/hooks/block-dangerous.js +62 -62
- package/src/hooks/cfn-lint-after-edit-eval.js +30 -30
- package/src/hooks/cfn-lint-after-edit.js +81 -81
- package/src/hooks/check-catalog-fresh-eval.js +63 -63
- package/src/hooks/check-catalog-fresh.js +33 -33
- package/src/hooks/session-context-eval.js +81 -81
- package/src/hooks/session-context.js +41 -41
- package/src/hooks/subagent-context-eval.js +44 -44
- package/src/hooks/subagent-context.js +48 -48
- package/src/lib/audit-synthesis.js +24 -24
- package/src/lib/aws-client.js +15 -15
- package/src/lib/cfn-yaml.js +92 -92
- package/src/lib/config.js +76 -76
- package/src/lib/constants.js +85 -85
- package/src/lib/defaults.js +16 -16
- package/src/lib/install-workflow.js +28 -28
- package/src/lib/kit-source.js +43 -5
- package/src/lib/license-client.js +84 -84
- package/src/lib/license-config.js +162 -162
- package/src/lib/license-store.js +43 -8
- package/src/lib/license.js +163 -150
- package/src/lib/lint-overrides.js +226 -226
- package/src/lib/logger.js +16 -16
- package/src/lib/project-config.js +145 -145
- package/src/lib/providers/polar.js +215 -215
- package/src/lib/session-state.js +91 -91
package/README.md
CHANGED
|
@@ -1,120 +1,141 @@
|
|
|
1
|
-
# Clawform
|
|
2
|
-
|
|
3
|
-
**Safe CloudFormation workflows for Claude Code.**
|
|
4
|
-
|
|
5
|
-

|
|
6
|
-
|
|
7
|
-
Claude writes CloudFormation fast — and just as confidently when it's wrong.
|
|
8
|
-
Clawform is the layer between the agent and your AWS account.
|
|
9
|
-
|
|
10
|
-
It gives Claude the conventions to read before it writes, so templates come out
|
|
11
|
-
named to your standard, tagged, and with retention set on every stateful
|
|
12
|
-
resource. Every AWS change then goes through the `clawform` CLI: create a
|
|
13
|
-
changeset, show you the diff, execute only after you confirm. Destructive
|
|
14
|
-
operations and the stacks you've marked frozen are refused.
|
|
15
|
-
|
|
16
|
-
CloudFormation only — by design. Not Terraform, not CDK, not Pulumi.
|
|
17
|
-
|
|
18
|
-
**Try it free for 14 days** — the full kit, email-only checkout, no card:
|
|
19
|
-
<https://clawform.thejoseki.com/?utm_source=npm>. When the trial lapses, `deploy` and
|
|
20
|
-
`rollback` keep working; an expired licence never strands a stack.
|
|
21
|
-
|
|
22
|
-
## Quickstart (10 minutes)
|
|
23
|
-
|
|
24
|
-
```bash
|
|
25
|
-
# 1. Install the CLI (adds `clawform` to your PATH)
|
|
26
|
-
npm i -g @thejoseki/clawform
|
|
27
|
-
|
|
28
|
-
# 2. Activate your key — free 14-day trial or purchase, both from the site
|
|
29
|
-
clawform activate <your-license-key>
|
|
30
|
-
|
|
31
|
-
# 3. Install the Claude Code plugin — the CLI unpacks it from your activated copy
|
|
32
|
-
clawform init plugin # --global (default) or --project
|
|
33
|
-
```
|
|
34
|
-
|
|
35
|
-
Activation claims one of your seat's device slots; free one with
|
|
36
|
-
`clawform deactivate` when you retire a machine
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
#
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
1
|
+
# Clawform
|
|
2
|
+
|
|
3
|
+
**Safe CloudFormation workflows for Claude Code.**
|
|
4
|
+
|
|
5
|
+

|
|
6
|
+
|
|
7
|
+
Claude writes CloudFormation fast — and just as confidently when it's wrong.
|
|
8
|
+
Clawform is the layer between the agent and your AWS account.
|
|
9
|
+
|
|
10
|
+
It gives Claude the conventions to read before it writes, so templates come out
|
|
11
|
+
named to your standard, tagged, and with retention set on every stateful
|
|
12
|
+
resource. Every AWS change then goes through the `clawform` CLI: create a
|
|
13
|
+
changeset, show you the diff, execute only after you confirm. Destructive
|
|
14
|
+
operations and the stacks you've marked frozen are refused.
|
|
15
|
+
|
|
16
|
+
CloudFormation only — by design. Not Terraform, not CDK, not Pulumi.
|
|
17
|
+
|
|
18
|
+
**Try it free for 14 days** — the full kit, email-only checkout, no card:
|
|
19
|
+
<https://clawform.thejoseki.com/?utm_source=npm>. When the trial lapses, `deploy` and
|
|
20
|
+
`rollback` keep working; an expired licence never strands a stack.
|
|
21
|
+
|
|
22
|
+
## Quickstart (10 minutes)
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
# 1. Install the CLI (adds `clawform` to your PATH)
|
|
26
|
+
npm i -g @thejoseki/clawform
|
|
27
|
+
|
|
28
|
+
# 2. Activate your key — free 14-day trial or purchase, both from the site
|
|
29
|
+
clawform activate <your-license-key>
|
|
30
|
+
|
|
31
|
+
# 3. Install the Claude Code plugin — the CLI unpacks it from your activated copy
|
|
32
|
+
clawform init plugin # --global (default) or --project
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
Activation claims one of your seat's device slots; free one with
|
|
36
|
+
`clawform deactivate` when you retire a machine (re-activating the same machine
|
|
37
|
+
re-adopts its existing slot — it does not take a second). **In CI, do not
|
|
38
|
+
activate** — set `CLAWFORM_LICENSE_KEY` in the environment instead, which
|
|
39
|
+
validates per-run without taking a slot.
|
|
40
|
+
|
|
41
|
+
Upgrading later? After `npm i -g @thejoseki/clawform@latest`, run
|
|
42
|
+
`clawform update` to pull the newest skills (it force-refreshes the plugin kit),
|
|
43
|
+
then restart Claude Code.
|
|
44
|
+
|
|
45
|
+
Then, **in your own project**:
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
# 4. Create your config — customer, region, accounts, legacy prefixes
|
|
49
|
+
clawform init # interactive wizard, writes clawform.config.json
|
|
50
|
+
|
|
51
|
+
# 5. Point Clawform at your AWS profile (per-user, gitignored)
|
|
52
|
+
# .claude/settings.local.json → env block → AWS_PROFILE
|
|
53
|
+
|
|
54
|
+
# 6. Cache your account's stacks and exports (run before your first deploy,
|
|
55
|
+
# and after every successful one)
|
|
56
|
+
clawform sync
|
|
57
|
+
|
|
58
|
+
# 7. Open Claude Code and scaffold your first stack
|
|
59
|
+
claude
|
|
60
|
+
> /clawform:scaffold-project # propose architecture + deploy order
|
|
61
|
+
> /clawform:new-stack networking d
|
|
62
|
+
> /clawform:validate cfn/d-acme-networking.yaml
|
|
63
|
+
> /clawform:deploy cfn/d-acme-networking.yaml d
|
|
64
|
+
```
|
|
65
|
+
|
|
66
|
+
That's a real stack, deployed through a changeset you confirmed.
|
|
67
|
+
|
|
68
|
+
### Don't know which services you need?
|
|
69
|
+
|
|
70
|
+
`/clawform:scaffold-project` detects that case and asks a different set of questions —
|
|
71
|
+
about consequences rather than services. *"If this were down for two hours on a Sunday
|
|
72
|
+
night, would anyone lose money?"* rather than *"Fargate or Lambda?"* Every question has an
|
|
73
|
+
**"I'm not sure"** option that states the default it applies and what that default costs.
|
|
74
|
+
|
|
75
|
+
Your monthly spending ceiling is the first question, and it eliminates candidate
|
|
76
|
+
architectures **before** any design work — so you never spend half an hour agreeing to
|
|
77
|
+
something you cannot pay for. Nothing is priced from memory: figures come from the AWS
|
|
78
|
+
Pricing API, and if one cannot be fetched you are told so instead of being given a guess.
|
|
79
|
+
|
|
80
|
+
The interview runs in at most three rounds and its answers are written to
|
|
81
|
+
`docs/scaffold/*.json`, so an interrupted session resumes where it stopped rather than
|
|
82
|
+
starting over. **Those files are committed** — that is what makes the design reviewable in
|
|
83
|
+
a pull request — and `requirements.json` contains your spending ceiling and a
|
|
84
|
+
plain-language description of what you are building. On a public repository, decide
|
|
85
|
+
whether you want that before you push.
|
|
86
|
+
|
|
87
|
+
## Configuration
|
|
88
|
+
|
|
89
|
+
Clawform reads two files. Neither hardcodes anything about your organization.
|
|
90
|
+
|
|
91
|
+
| File | Scope | Committed? | Holds |
|
|
92
|
+
|---|---|---|---|
|
|
93
|
+
| `clawform.config.json` | per-project | yes | customer, region, accounts, `legacy_prefixes` |
|
|
94
|
+
| `.claude/settings.local.json` | per-user | no (gitignored) | `AWS_PROFILE`, owner/cost-center overrides |
|
|
95
|
+
|
|
96
|
+
`clawform init` writes the first one. For CI or scripted bootstrap, set
|
|
97
|
+
`CLAWFORM_INIT_CUSTOMER=… CLAWFORM_INIT_REGION=… clawform init`.
|
|
98
|
+
|
|
99
|
+
A fresh project with an empty `legacy_prefixes` has no legacy-freeze gate — that
|
|
100
|
+
is correct. Clawform never imposes another project's freeze list on you.
|
|
101
|
+
|
|
102
|
+
## CI/CD
|
|
103
|
+
|
|
104
|
+
`clawform ci init` generates a GitHub Actions workflow or a GitLab CI config that
|
|
105
|
+
lints your templates and, on the protected branch, hands a source bundle to the
|
|
106
|
+
`cicd-pipeline` archetype's CodePipeline (validate → manual approval → execute).
|
|
107
|
+
|
|
108
|
+
- `--provider gitlab | github` — your Git host (SaaS or self-managed).
|
|
109
|
+
- `--auth keys | oidc` — `keys` (default) stores IAM access keys as CI secrets;
|
|
110
|
+
`--auth oidc` is keyless — the job assumes an IAM role through the host's OIDC
|
|
111
|
+
issuer (GitHub or GitLab SaaS) and uses short-lived credentials, so there are
|
|
112
|
+
no AWS keys to store. OIDC needs a `--role-arn` and a one-time AWS-side trust
|
|
113
|
+
policy you own; a self-managed GitLab uses `--auth keys`.
|
|
114
|
+
|
|
115
|
+
Full trust-policy setup and the keys→OIDC cutover are in the runbook
|
|
116
|
+
(`docs/runbook.md`, bundled with the plugin).
|
|
117
|
+
|
|
118
|
+
## Requirements
|
|
119
|
+
|
|
120
|
+
- Node.js ≥ 20 LTS
|
|
121
|
+
- AWS CLI v2
|
|
122
|
+
- Claude Code CLI
|
|
123
|
+
- *(recommended)* `cfn-lint` — `pip install cfn-lint`
|
|
124
|
+
- *(optional)* `cfn-guard` 3.x for Clawform's custom rules
|
|
125
|
+
|
|
126
|
+
## Links
|
|
127
|
+
|
|
128
|
+
- **[clawform.thejoseki.com](https://clawform.thejoseki.com)** — the product
|
|
129
|
+
page: what Clawform does, per-seat pricing, and how to get a license.
|
|
130
|
+
- **[thejoseki.com](https://thejoseki.com)** — Joseki, the vendor behind
|
|
131
|
+
Clawform. Known-good moves for agentic work.
|
|
132
|
+
|
|
133
|
+
## License
|
|
134
|
+
|
|
135
|
+
Commercial, per-seat. One seat per developer; no resale or redistribution.
|
|
136
|
+
Provided **as is**, with no warranty and no liability for AWS charges — you are
|
|
137
|
+
responsible for reviewing every change before it deploys and for your own cloud
|
|
138
|
+
spend. Full terms in the `LICENSE` file.
|
|
139
|
+
|
|
140
|
+
Clawform is not affiliated with Amazon Web Services, Inc. "AWS" and
|
|
141
|
+
"CloudFormation" are trademarks of Amazon.com, Inc. or its affiliates.
|
package/bin/clawform.js
CHANGED
|
@@ -1,151 +1,161 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { Command } from 'commander';
|
|
3
|
-
import { readLicense } from '../src/lib/license-store.js';
|
|
4
|
-
import { versionWatermark } from '../src/lib/watermark.js';
|
|
5
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
6
|
-
import { join } from 'node:path';
|
|
7
|
-
import { logger } from '../src/lib/logger.js';
|
|
8
|
-
import pkg from '../package.json' with { type: 'json' };
|
|
9
|
-
|
|
10
|
-
// Inject .claude/settings.local.json env block into process.env before any
|
|
11
|
-
// command runs. Claude Code applies this env to its own tool invocations,
|
|
12
|
-
// but when the CLI is spawned via Bash (or run directly) it must self-load.
|
|
13
|
-
loadSettingsEnv(process.cwd());
|
|
14
|
-
|
|
15
|
-
function loadSettingsEnv(cwd) {
|
|
16
|
-
const path = join(cwd, '.claude', 'settings.local.json');
|
|
17
|
-
if (!existsSync(path)) return;
|
|
18
|
-
try {
|
|
19
|
-
const settings = JSON.parse(readFileSync(path, 'utf8'));
|
|
20
|
-
for (const [k, v] of Object.entries(settings.env ?? {})) {
|
|
21
|
-
if (!process.env[k] && typeof v === 'string') process.env[k] = v;
|
|
22
|
-
}
|
|
23
|
-
} catch {
|
|
24
|
-
// Silent — loadConfig() will surface missing creds with an actionable message.
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const program = new Command();
|
|
29
|
-
|
|
30
|
-
program
|
|
31
|
-
.name('clawform')
|
|
32
|
-
.description('Clawform — safe CloudFormation workflows for Claude Code')
|
|
33
|
-
// Version carries the ownership watermark (holder + key suffix) so a shared
|
|
34
|
-
// install names its source. readLicense fails open, so this never throws.
|
|
35
|
-
.version(pkg.version + versionWatermark(readLicense()), '-v, --version');
|
|
36
|
-
|
|
37
|
-
const lazy = (mod) => async (...args) => {
|
|
38
|
-
const { default: handler } = await import(`../src/commands/${mod}.js`);
|
|
39
|
-
return runSafe(() => handler(...args));
|
|
40
|
-
};
|
|
41
|
-
|
|
42
|
-
const init = program
|
|
43
|
-
.command('init')
|
|
44
|
-
.description('Scaffold a new AWS project (greenfield wizard). Non-interactive when CLAWFORM_INIT_* env vars are set.')
|
|
45
|
-
.option('--force', 'Overwrite an existing clawform.config.json')
|
|
46
|
-
.action(async (opts) => lazy('init')(opts));
|
|
47
|
-
|
|
48
|
-
init
|
|
49
|
-
.command('plugin')
|
|
50
|
-
.description('Install the Clawform Claude Code plugin (decrypts the bundled content; requires an active license once enforcement is on)')
|
|
51
|
-
.option('--global', 'Install for all projects (~/.claude) — default')
|
|
52
|
-
.option('--project', 'Install for the current project only (./.claude)')
|
|
53
|
-
.
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
.
|
|
58
|
-
.
|
|
59
|
-
.option('--
|
|
60
|
-
.action(async (opts) => lazy('
|
|
61
|
-
|
|
62
|
-
program
|
|
63
|
-
.command('
|
|
64
|
-
.description('
|
|
65
|
-
.option('--
|
|
66
|
-
.
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
.
|
|
71
|
-
.
|
|
72
|
-
.
|
|
73
|
-
.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
.
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
.
|
|
80
|
-
.
|
|
81
|
-
.option('--
|
|
82
|
-
.
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
.
|
|
87
|
-
.
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
.
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
.
|
|
98
|
-
.
|
|
99
|
-
.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
.
|
|
105
|
-
.
|
|
106
|
-
.
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
.
|
|
112
|
-
.
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
.
|
|
117
|
-
.
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
.
|
|
123
|
-
.
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
.
|
|
129
|
-
.
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
.
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
.
|
|
136
|
-
.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
.
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { Command, Option } from 'commander';
|
|
3
|
+
import { readLicense } from '../src/lib/license-store.js';
|
|
4
|
+
import { versionWatermark } from '../src/lib/watermark.js';
|
|
5
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
6
|
+
import { join } from 'node:path';
|
|
7
|
+
import { logger } from '../src/lib/logger.js';
|
|
8
|
+
import pkg from '../package.json' with { type: 'json' };
|
|
9
|
+
|
|
10
|
+
// Inject .claude/settings.local.json env block into process.env before any
|
|
11
|
+
// command runs. Claude Code applies this env to its own tool invocations,
|
|
12
|
+
// but when the CLI is spawned via Bash (or run directly) it must self-load.
|
|
13
|
+
loadSettingsEnv(process.cwd());
|
|
14
|
+
|
|
15
|
+
function loadSettingsEnv(cwd) {
|
|
16
|
+
const path = join(cwd, '.claude', 'settings.local.json');
|
|
17
|
+
if (!existsSync(path)) return;
|
|
18
|
+
try {
|
|
19
|
+
const settings = JSON.parse(readFileSync(path, 'utf8'));
|
|
20
|
+
for (const [k, v] of Object.entries(settings.env ?? {})) {
|
|
21
|
+
if (!process.env[k] && typeof v === 'string') process.env[k] = v;
|
|
22
|
+
}
|
|
23
|
+
} catch {
|
|
24
|
+
// Silent — loadConfig() will surface missing creds with an actionable message.
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
const program = new Command();
|
|
29
|
+
|
|
30
|
+
program
|
|
31
|
+
.name('clawform')
|
|
32
|
+
.description('Clawform — safe CloudFormation workflows for Claude Code')
|
|
33
|
+
// Version carries the ownership watermark (holder + key suffix) so a shared
|
|
34
|
+
// install names its source. readLicense fails open, so this never throws.
|
|
35
|
+
.version(pkg.version + versionWatermark(readLicense()), '-v, --version');
|
|
36
|
+
|
|
37
|
+
const lazy = (mod) => async (...args) => {
|
|
38
|
+
const { default: handler } = await import(`../src/commands/${mod}.js`);
|
|
39
|
+
return runSafe(() => handler(...args));
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
const init = program
|
|
43
|
+
.command('init')
|
|
44
|
+
.description('Scaffold a new AWS project (greenfield wizard). Non-interactive when CLAWFORM_INIT_* env vars are set.')
|
|
45
|
+
.option('--force', 'Overwrite an existing clawform.config.json')
|
|
46
|
+
.action(async (opts) => lazy('init')(opts));
|
|
47
|
+
|
|
48
|
+
init
|
|
49
|
+
.command('plugin')
|
|
50
|
+
.description('Install the Clawform Claude Code plugin (decrypts the bundled content; requires an active license once enforcement is on)')
|
|
51
|
+
.option('--global', 'Install for all projects (~/.claude) — default')
|
|
52
|
+
.option('--project', 'Install for the current project only (./.claude)')
|
|
53
|
+
.option('--refresh', 'Re-fetch the plugin kit from the service, ignoring the local cache (gets the newest skills)')
|
|
54
|
+
.action(async (opts) => lazy('init-plugin')({ scope: opts.project ? 'project' : 'global', refresh: Boolean(opts.refresh) }));
|
|
55
|
+
|
|
56
|
+
program
|
|
57
|
+
.command('update')
|
|
58
|
+
.description('Refresh the installed plugin to the newest skills/commands (force re-fetches the kit) and remind you to update the CLI')
|
|
59
|
+
.option('--project', 'Refresh the project-scoped install (./.claude) instead of global')
|
|
60
|
+
.action(async (opts) => lazy('update')({ scope: opts.project ? 'project' : 'global' }));
|
|
61
|
+
|
|
62
|
+
program
|
|
63
|
+
.command('sync')
|
|
64
|
+
.description('Refresh outputs-catalog.md and stacks-inventory.md from live AWS')
|
|
65
|
+
.option('--profile <name>', 'AWS profile (overrides .claude/settings.local.json env block)')
|
|
66
|
+
.option('--region <name>', 'AWS region (overrides .claude/settings.local.json env block)')
|
|
67
|
+
.action(async (opts) => lazy('sync')(opts));
|
|
68
|
+
|
|
69
|
+
program
|
|
70
|
+
.command('validate <file>')
|
|
71
|
+
.description('Run cfn-lint, cfn-guard (rules.guard + lint-overrides), cfn-nag, and CloudFormation ValidateTemplate on a YAML template')
|
|
72
|
+
.option('--skip-aws', 'Skip the live ValidateTemplate call (CI / fresh box without AWS creds)')
|
|
73
|
+
.action(async (file, opts) => lazy('validate')({ file, ...opts }));
|
|
74
|
+
|
|
75
|
+
program
|
|
76
|
+
.command('deploy <file> <env>')
|
|
77
|
+
.description('Deploy a template to <env> via CloudFormation changeset (create → confirm → execute)')
|
|
78
|
+
.requiredOption('--stack-name <name>', 'CFN stack name (e.g., d-acme-clawtest-fanout)')
|
|
79
|
+
.requiredOption('--project <name>', 'Project shortname for Project tag (e.g., clawtest)')
|
|
80
|
+
.option('--params <path>', 'JSON file: [{ParameterKey, ParameterValue}, ...]')
|
|
81
|
+
.option('--capabilities <list>', 'Comma-separated: CAPABILITY_IAM, CAPABILITY_NAMED_IAM, CAPABILITY_AUTO_EXPAND')
|
|
82
|
+
.option('--auto-approve', 'Skip confirmation (refused on Replace or data-resource removal)')
|
|
83
|
+
.action(async (file, env, opts) => lazy('deploy')({ file, env, ...opts }));
|
|
84
|
+
|
|
85
|
+
program
|
|
86
|
+
.command('diff <stack>')
|
|
87
|
+
.description('Diff local template vs live stack')
|
|
88
|
+
.option('--file <path>', 'Local template file (defaults to cfn/<stack>.yaml)')
|
|
89
|
+
.action(async (stack, opts) => lazy('diff')({ stack, ...opts }));
|
|
90
|
+
|
|
91
|
+
program
|
|
92
|
+
.command('rollback <stack>')
|
|
93
|
+
.description('Recover a stack stuck in CREATE_FAILED / UPDATE_ROLLBACK_FAILED')
|
|
94
|
+
.action(async (stack, opts) => lazy('rollback')({ stack, ...opts }));
|
|
95
|
+
|
|
96
|
+
program
|
|
97
|
+
.command('drift <stack>')
|
|
98
|
+
.description('Detect configuration drift on a deployed stack')
|
|
99
|
+
.action(async (stack, opts) => lazy('drift')({ stack, ...opts }));
|
|
100
|
+
|
|
101
|
+
program
|
|
102
|
+
.command('status')
|
|
103
|
+
.description('Day-2 health board: per-project stack status, last deploy, last-known drift, monitoring gap')
|
|
104
|
+
.option('--project <name>', 'Project shortname to filter to (default: all)')
|
|
105
|
+
.option('--env <env>', 'Env to filter to (d | stg | p)')
|
|
106
|
+
.option('--profile <name>', 'AWS profile (overrides .claude/settings.local.json env block)')
|
|
107
|
+
.option('--region <name>', 'AWS region (overrides .claude/settings.local.json env block)')
|
|
108
|
+
.action(async (opts) => lazy('status')(opts));
|
|
109
|
+
|
|
110
|
+
program
|
|
111
|
+
.command('activate <key>')
|
|
112
|
+
.description('Activate a license key on this machine (one activation slot; CI should use CLAWFORM_LICENSE_KEY instead)')
|
|
113
|
+
.action(async (key) => lazy('activate')({ key }));
|
|
114
|
+
|
|
115
|
+
program
|
|
116
|
+
.command('license')
|
|
117
|
+
.description('Show license status on this machine. --check exits 1 when unlicensed (for scripts/skills)')
|
|
118
|
+
.option('--check', 'Silent success when active; exit 1 when not')
|
|
119
|
+
.action(async (opts) => lazy('license-status')(opts));
|
|
120
|
+
|
|
121
|
+
program
|
|
122
|
+
.command('deactivate')
|
|
123
|
+
.description('Release this machine\'s activation slot and clear the local license')
|
|
124
|
+
.action(async () => lazy('deactivate')({}));
|
|
125
|
+
|
|
126
|
+
program
|
|
127
|
+
.command('cost-report')
|
|
128
|
+
.description('Month-to-date AWS spend grouped by Project / Env / CostCenter tag (or service) via Cost Explorer')
|
|
129
|
+
.option('--month <YYYY-MM>', 'Month to report (defaults to the current month, month-to-date)')
|
|
130
|
+
.option('--group-by <key>', 'project | env | costcenter | service', 'project')
|
|
131
|
+
.option('--profile <name>', 'AWS profile (overrides .claude/settings.local.json env block)')
|
|
132
|
+
.action(async (opts) => lazy('cost-report')(opts));
|
|
133
|
+
|
|
134
|
+
const ci = program
|
|
135
|
+
.command('ci')
|
|
136
|
+
.description('CI/CD scaffolding: bundle the repo to the pipeline SourceBucket on the protected branch (GitHub or GitLab, SaaS or self-managed)');
|
|
137
|
+
|
|
138
|
+
ci
|
|
139
|
+
.command('init')
|
|
140
|
+
.description('Generate CI config (GitHub Actions or GitLab CI): cfn-lint on every MR/PR, then upload source.zip to the pipeline bucket on the protected branch. Creds via stored IAM keys or keyless OIDC (--auth)')
|
|
141
|
+
.addOption(new Option('--provider <name>', 'Git host').choices(['gitlab', 'github']).default('gitlab'))
|
|
142
|
+
.addOption(new Option('--auth <mode>', 'keys (stored IAM keys, default) | oidc (keyless, github.com/gitlab.com)').choices(['keys', 'oidc']).default('keys'))
|
|
143
|
+
.option('--role-arn <arn>', 'IAM role to assume via OIDC (required with --auth oidc)')
|
|
144
|
+
.option('--region <name>', 'AWS region for the upload (--auth oidc; defaults to clawform.config.json region)')
|
|
145
|
+
.option('--source-bucket <name>', 'Pipeline SourceBucket name (the cicd-pipeline stack\'s SourceBucketName output)')
|
|
146
|
+
.option('--cfn-dir <dir>', 'Directory holding cfn templates', 'cfn')
|
|
147
|
+
.option('--branch <name>', 'Protected branch that triggers the deploy upload', 'main')
|
|
148
|
+
.option('--force', 'Overwrite an existing CI config')
|
|
149
|
+
.action(async (opts) => lazy('ci-init')({ provider: opts.provider, auth: opts.auth, roleArn: opts.roleArn, region: opts.region, sourceBucket: opts.sourceBucket, cfnDir: opts.cfnDir, branch: opts.branch, force: opts.force }));
|
|
150
|
+
|
|
151
|
+
async function runSafe(fn) {
|
|
152
|
+
try {
|
|
153
|
+
await fn();
|
|
154
|
+
} catch (err) {
|
|
155
|
+
logger.error(err.message || String(err));
|
|
156
|
+
if (process.env.CLAWFORM_DEBUG) console.error(err.stack);
|
|
157
|
+
process.exit(1);
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
program.parseAsync(process.argv);
|