@thejoseki/clawform 2.3.0 → 2.3.2

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 (48) hide show
  1. package/README.md +141 -120
  2. package/bin/clawform.js +161 -151
  3. package/package.json +65 -65
  4. package/src/aws/catalog.js +116 -116
  5. package/src/aws/changeset.js +231 -231
  6. package/src/aws/drift.js +59 -59
  7. package/src/aws/exports.js +213 -213
  8. package/src/aws/inventory.js +156 -156
  9. package/src/commands/activate.js +34 -2
  10. package/src/commands/ci-init.js +393 -212
  11. package/src/commands/cost-report.js +163 -163
  12. package/src/commands/deactivate.js +9 -2
  13. package/src/commands/deploy.js +413 -413
  14. package/src/commands/diff.js +39 -39
  15. package/src/commands/drift.js +35 -35
  16. package/src/commands/init-plugin.js +6 -6
  17. package/src/commands/init.js +360 -360
  18. package/src/commands/rollback.js +126 -126
  19. package/src/commands/status.js +147 -147
  20. package/src/commands/sync.js +50 -50
  21. package/src/commands/update.js +24 -0
  22. package/src/commands/validate.js +349 -349
  23. package/src/hooks/block-dangerous.js +62 -62
  24. package/src/hooks/cfn-lint-after-edit-eval.js +30 -30
  25. package/src/hooks/cfn-lint-after-edit.js +81 -81
  26. package/src/hooks/check-catalog-fresh-eval.js +63 -63
  27. package/src/hooks/check-catalog-fresh.js +33 -33
  28. package/src/hooks/session-context-eval.js +81 -81
  29. package/src/hooks/session-context.js +41 -41
  30. package/src/hooks/subagent-context-eval.js +44 -44
  31. package/src/hooks/subagent-context.js +48 -48
  32. package/src/lib/audit-synthesis.js +24 -24
  33. package/src/lib/aws-client.js +15 -15
  34. package/src/lib/cfn-yaml.js +92 -92
  35. package/src/lib/config.js +76 -76
  36. package/src/lib/constants.js +85 -85
  37. package/src/lib/defaults.js +16 -16
  38. package/src/lib/install-workflow.js +28 -28
  39. package/src/lib/kit-source.js +43 -5
  40. package/src/lib/license-client.js +84 -84
  41. package/src/lib/license-config.js +162 -162
  42. package/src/lib/license-store.js +43 -8
  43. package/src/lib/license.js +15 -2
  44. package/src/lib/lint-overrides.js +226 -226
  45. package/src/lib/logger.js +16 -16
  46. package/src/lib/project-config.js +145 -145
  47. package/src/lib/providers/polar.js +215 -215
  48. package/src/lib/session-state.js +91 -91
package/package.json CHANGED
@@ -1,65 +1,65 @@
1
- {
2
- "name": "@thejoseki/clawform",
3
- "version": "2.3.0",
4
- "description": "Clawform — safe CloudFormation workflows for Claude Code",
5
- "homepage": "https://clawform.thejoseki.com",
6
- "type": "module",
7
- "license": "SEE LICENSE IN LICENSE",
8
- "author": "Joseki <support@thejoseki.com>",
9
- "publishConfig": {
10
- "access": "public"
11
- },
12
- "bin": {
13
- "clawform": "bin/clawform.js"
14
- },
15
- "engines": {
16
- "node": ">=20.10"
17
- },
18
- "scripts": {
19
- "test": "vitest run",
20
- "test:watch": "vitest",
21
- "test:eval": "vitest run tests/eval",
22
- "lint": "node scripts/lint-content.js",
23
- "package": "node scripts/package-plugin.js && node scripts/pack-payload.js",
24
- "kit:build": "node scripts/package-plugin.js && node scripts/build-kit-worker.js",
25
- "kit:deploy": "npm run kit:build && npm --prefix services/kit-worker run deploy",
26
- "prepublishOnly": "node scripts/check-release-mode.js && npm run package",
27
- "clean-room": "node scripts/clean-room.js",
28
- "e2e": "node scripts/e2e-licensed-install.js",
29
- "e2e:trial": "node scripts/e2e-trial.js",
30
- "test:coverage": "vitest run --coverage --coverage.reporter=text-summary --coverage.include=\"**/*.js\" --coverage.exclude=\"tests/**\" --coverage.exclude=\"skills/**\""
31
- },
32
- "dependencies": {
33
- "@aws-sdk/client-cloudformation": "^3.700.0",
34
- "@aws-sdk/client-cost-explorer": "^3.1066.0",
35
- "@aws-sdk/client-sts": "^3.700.0",
36
- "chalk": "^5.3.0",
37
- "commander": "^12.1.0",
38
- "diff": "^7.0.0",
39
- "execa": "^9.5.0",
40
- "inquirer": "^12.0.0",
41
- "js-yaml": "^4.1.0"
42
- },
43
- "devDependencies": {
44
- "@vitest/coverage-v8": "^2.1.9",
45
- "vitest": "^2.1.9"
46
- },
47
- "files": [
48
- "bin/",
49
- "src/"
50
- ],
51
- "keywords": [
52
- "claude-code",
53
- "claude",
54
- "cloudformation",
55
- "aws",
56
- "iac",
57
- "infrastructure-as-code",
58
- "aws-cdk-alternative",
59
- "changeset",
60
- "cfn-lint",
61
- "devops",
62
- "guardrails",
63
- "ai-agent"
64
- ]
65
- }
1
+ {
2
+ "name": "@thejoseki/clawform",
3
+ "version": "2.3.2",
4
+ "description": "Clawform — safe CloudFormation workflows for Claude Code",
5
+ "homepage": "https://clawform.thejoseki.com",
6
+ "type": "module",
7
+ "license": "SEE LICENSE IN LICENSE",
8
+ "author": "Joseki <support@thejoseki.com>",
9
+ "publishConfig": {
10
+ "access": "public"
11
+ },
12
+ "bin": {
13
+ "clawform": "bin/clawform.js"
14
+ },
15
+ "engines": {
16
+ "node": ">=20.10"
17
+ },
18
+ "scripts": {
19
+ "test": "vitest run",
20
+ "test:watch": "vitest",
21
+ "test:eval": "vitest run tests/eval",
22
+ "lint": "node scripts/lint-content.js",
23
+ "package": "node scripts/package-plugin.js && node scripts/pack-payload.js",
24
+ "kit:build": "node scripts/package-plugin.js && node scripts/build-kit-worker.js",
25
+ "kit:deploy": "npm run kit:build && npm --prefix services/kit-worker run deploy",
26
+ "prepublishOnly": "node scripts/check-release-mode.js && npm run kit:deploy && npm run package",
27
+ "clean-room": "node scripts/clean-room.js",
28
+ "e2e": "node scripts/e2e-licensed-install.js",
29
+ "e2e:trial": "node scripts/e2e-trial.js",
30
+ "test:coverage": "vitest run --coverage --coverage.reporter=text-summary --coverage.include=\"**/*.js\" --coverage.exclude=\"tests/**\" --coverage.exclude=\"skills/**\""
31
+ },
32
+ "dependencies": {
33
+ "@aws-sdk/client-cloudformation": "^3.700.0",
34
+ "@aws-sdk/client-cost-explorer": "^3.1066.0",
35
+ "@aws-sdk/client-sts": "^3.700.0",
36
+ "chalk": "^5.3.0",
37
+ "commander": "^12.1.0",
38
+ "diff": "^7.0.0",
39
+ "execa": "^9.5.0",
40
+ "inquirer": "^12.0.0",
41
+ "js-yaml": "^4.1.0"
42
+ },
43
+ "devDependencies": {
44
+ "@vitest/coverage-v8": "^2.1.9",
45
+ "vitest": "^2.1.9"
46
+ },
47
+ "files": [
48
+ "bin/",
49
+ "src/"
50
+ ],
51
+ "keywords": [
52
+ "claude-code",
53
+ "claude",
54
+ "cloudformation",
55
+ "aws",
56
+ "iac",
57
+ "infrastructure-as-code",
58
+ "aws-cdk-alternative",
59
+ "changeset",
60
+ "cfn-lint",
61
+ "devops",
62
+ "guardrails",
63
+ "ai-agent"
64
+ ]
65
+ }
@@ -1,116 +1,116 @@
1
- import { paginateListExports } from '@aws-sdk/client-cloudformation';
2
- import { ENVS, VISIBILITY_PREFIXES, SERVICE_SHORTNAMES } from '../lib/constants.js';
3
-
4
- const ENV_PREFIX_RE = new RegExp(`^(${ENVS.join('|')})-`);
5
- const VISIBILITY = new Set(VISIBILITY_PREFIXES);
6
- const SERVICES = new Set(SERVICE_SHORTNAMES);
7
-
8
- export function parseExport(name) {
9
- const m = ENV_PREFIX_RE.exec(name);
10
- if (!m) return { env: null, project: null };
11
- const tokens = name.split('-');
12
- if (tokens.length < 3) return { env: null, project: null };
13
-
14
- let projectIdx;
15
- if (VISIBILITY.has(tokens[2])) projectIdx = 4; // <env>-<cust>-<pri|pub>-<svc>-<project>
16
- else if (SERVICES.has(tokens[2])) projectIdx = 3; // <env>-<cust>-<svc>-<project>
17
- else projectIdx = 2; // <env>-<cust>-<project>-<feature>
18
-
19
- const project = tokens[projectIdx];
20
- if (!project) return { env: null, project: null };
21
- return { env: m[1], project };
22
- }
23
-
24
- export async function fetchExports(client) {
25
- const exports = [];
26
- for await (const page of paginateListExports({ client }, {})) {
27
- for (const ex of page.Exports ?? []) {
28
- exports.push({
29
- name: ex.Name,
30
- value: ex.Value,
31
- exportingStackId: ex.ExportingStackId,
32
- });
33
- }
34
- }
35
- return exports;
36
- }
37
-
38
- export function groupExports(exports) {
39
- const byProject = new Map();
40
- const uncategorized = [];
41
- for (const ex of exports) {
42
- const { env, project } = parseExport(ex.name);
43
- if (!env || !project) {
44
- uncategorized.push(ex);
45
- continue;
46
- }
47
- if (!byProject.has(project)) byProject.set(project, new Map());
48
- const byEnv = byProject.get(project);
49
- if (!byEnv.has(env)) byEnv.set(env, []);
50
- byEnv.get(env).push(ex);
51
- }
52
- return { byProject, uncategorized };
53
- }
54
-
55
- function stackNameOf(exportingStackId) {
56
- if (!exportingStackId) return '(unknown)';
57
- const parts = exportingStackId.split('/');
58
- return parts[1] ?? '(unknown)';
59
- }
60
-
61
- function truncate(s, n) {
62
- if (!s) return '';
63
- return s.length > n ? s.slice(0, n - 1) + '…' : s;
64
- }
65
-
66
- function escapePipe(s) {
67
- return String(s ?? '').replace(/\|/g, '\\|');
68
- }
69
-
70
- function renderTable(items) {
71
- const lines = [];
72
- lines.push('| Export | Value preview | Source stack |');
73
- lines.push('|---|---|---|');
74
- const sorted = [...items].sort((a, b) => a.name.localeCompare(b.name));
75
- for (const it of sorted) {
76
- lines.push(
77
- `| \`${escapePipe(it.name)}\` | \`${escapePipe(truncate(it.value, 40))}\` | \`${escapePipe(stackNameOf(it.exportingStackId))}\` |`,
78
- );
79
- }
80
- return lines;
81
- }
82
-
83
- export function renderCatalog({ byProject, uncategorized }, meta) {
84
- const lines = [
85
- '# CloudFormation Outputs Catalog',
86
- '',
87
- `> Generated by \`clawform sync\` at ${meta.generatedAt}`,
88
- `> Account: ${meta.account} · Region: ${meta.region} · ${meta.total} exports`,
89
- '',
90
- ];
91
-
92
- const projects = [...byProject.keys()].sort();
93
- for (const project of projects) {
94
- lines.push(`## Project: ${project}`, '');
95
- const envs = [...byProject.get(project).keys()].sort();
96
- for (const env of envs) {
97
- lines.push(`### Env: ${env}`, '');
98
- lines.push(...renderTable(byProject.get(project).get(env)));
99
- lines.push('');
100
- }
101
- }
102
-
103
- if (uncategorized.length > 0) {
104
- lines.push(
105
- `## Uncategorized (non-conforming naming, ${uncategorized.length})`,
106
- '',
107
- '> Exports that don\'t match `<env>-<customer>-[<pri|pub>-]<service>-<project>-...`.',
108
- '> Usually legacy stacks (OLDNET-*, OLDAPP-*) or pre-convention exports (e.g. `d-vpc-acme-billing-id`).',
109
- '',
110
- );
111
- lines.push(...renderTable(uncategorized));
112
- lines.push('');
113
- }
114
-
115
- return lines.join('\n');
116
- }
1
+ import { paginateListExports } from '@aws-sdk/client-cloudformation';
2
+ import { ENVS, VISIBILITY_PREFIXES, SERVICE_SHORTNAMES } from '../lib/constants.js';
3
+
4
+ const ENV_PREFIX_RE = new RegExp(`^(${ENVS.join('|')})-`);
5
+ const VISIBILITY = new Set(VISIBILITY_PREFIXES);
6
+ const SERVICES = new Set(SERVICE_SHORTNAMES);
7
+
8
+ export function parseExport(name) {
9
+ const m = ENV_PREFIX_RE.exec(name);
10
+ if (!m) return { env: null, project: null };
11
+ const tokens = name.split('-');
12
+ if (tokens.length < 3) return { env: null, project: null };
13
+
14
+ let projectIdx;
15
+ if (VISIBILITY.has(tokens[2])) projectIdx = 4; // <env>-<cust>-<pri|pub>-<svc>-<project>
16
+ else if (SERVICES.has(tokens[2])) projectIdx = 3; // <env>-<cust>-<svc>-<project>
17
+ else projectIdx = 2; // <env>-<cust>-<project>-<feature>
18
+
19
+ const project = tokens[projectIdx];
20
+ if (!project) return { env: null, project: null };
21
+ return { env: m[1], project };
22
+ }
23
+
24
+ export async function fetchExports(client) {
25
+ const exports = [];
26
+ for await (const page of paginateListExports({ client }, {})) {
27
+ for (const ex of page.Exports ?? []) {
28
+ exports.push({
29
+ name: ex.Name,
30
+ value: ex.Value,
31
+ exportingStackId: ex.ExportingStackId,
32
+ });
33
+ }
34
+ }
35
+ return exports;
36
+ }
37
+
38
+ export function groupExports(exports) {
39
+ const byProject = new Map();
40
+ const uncategorized = [];
41
+ for (const ex of exports) {
42
+ const { env, project } = parseExport(ex.name);
43
+ if (!env || !project) {
44
+ uncategorized.push(ex);
45
+ continue;
46
+ }
47
+ if (!byProject.has(project)) byProject.set(project, new Map());
48
+ const byEnv = byProject.get(project);
49
+ if (!byEnv.has(env)) byEnv.set(env, []);
50
+ byEnv.get(env).push(ex);
51
+ }
52
+ return { byProject, uncategorized };
53
+ }
54
+
55
+ function stackNameOf(exportingStackId) {
56
+ if (!exportingStackId) return '(unknown)';
57
+ const parts = exportingStackId.split('/');
58
+ return parts[1] ?? '(unknown)';
59
+ }
60
+
61
+ function truncate(s, n) {
62
+ if (!s) return '';
63
+ return s.length > n ? s.slice(0, n - 1) + '…' : s;
64
+ }
65
+
66
+ function escapePipe(s) {
67
+ return String(s ?? '').replace(/\|/g, '\\|');
68
+ }
69
+
70
+ function renderTable(items) {
71
+ const lines = [];
72
+ lines.push('| Export | Value preview | Source stack |');
73
+ lines.push('|---|---|---|');
74
+ const sorted = [...items].sort((a, b) => a.name.localeCompare(b.name));
75
+ for (const it of sorted) {
76
+ lines.push(
77
+ `| \`${escapePipe(it.name)}\` | \`${escapePipe(truncate(it.value, 40))}\` | \`${escapePipe(stackNameOf(it.exportingStackId))}\` |`,
78
+ );
79
+ }
80
+ return lines;
81
+ }
82
+
83
+ export function renderCatalog({ byProject, uncategorized }, meta) {
84
+ const lines = [
85
+ '# CloudFormation Outputs Catalog',
86
+ '',
87
+ `> Generated by \`clawform sync\` at ${meta.generatedAt}`,
88
+ `> Account: ${meta.account} · Region: ${meta.region} · ${meta.total} exports`,
89
+ '',
90
+ ];
91
+
92
+ const projects = [...byProject.keys()].sort();
93
+ for (const project of projects) {
94
+ lines.push(`## Project: ${project}`, '');
95
+ const envs = [...byProject.get(project).keys()].sort();
96
+ for (const env of envs) {
97
+ lines.push(`### Env: ${env}`, '');
98
+ lines.push(...renderTable(byProject.get(project).get(env)));
99
+ lines.push('');
100
+ }
101
+ }
102
+
103
+ if (uncategorized.length > 0) {
104
+ lines.push(
105
+ `## Uncategorized (non-conforming naming, ${uncategorized.length})`,
106
+ '',
107
+ '> Exports that don\'t match `<env>-<customer>-[<pri|pub>-]<service>-<project>-...`.',
108
+ '> Usually legacy stacks (OLDNET-*, OLDAPP-*) or pre-convention exports (e.g. `d-vpc-acme-billing-id`).',
109
+ '',
110
+ );
111
+ lines.push(...renderTable(uncategorized));
112
+ lines.push('');
113
+ }
114
+
115
+ return lines.join('\n');
116
+ }