@lifeaitools/rdc-skills 0.9.31 → 0.9.32
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/.claude-plugin/plugin.json +1 -1
- package/commands/deploy.md +15 -1
- package/package.json +1 -1
- package/scripts/fixtures/guides/bad-guide.md +15 -0
- package/scripts/fixtures/guides-clean/good-guide.md +16 -0
- package/scripts/self-test.mjs +210 -0
- package/scripts/test-guide-validator.mjs +194 -0
- package/skills/deploy/SKILL.md +47 -1
- package/skills/terminal-config/SKILL.md +18 -18
package/commands/deploy.md
CHANGED
|
@@ -120,11 +120,25 @@ Severity rules:
|
|
|
120
120
|
|
|
121
121
|
`--fix` auto-remediates only: missing watch_paths, registry row updates, CF cache purges. Never touches env vars, DNS, or container config without explicit confirmation.
|
|
122
122
|
|
|
123
|
+
## Coolify Access — clauth + REST API
|
|
124
|
+
|
|
125
|
+
All Coolify operations use the clauth daemon and the Coolify REST API directly.
|
|
126
|
+
There is no Coolify MCP server — do not reference `@masonator/coolify-mcp`.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
_COOLIFY=$(curl -s http://127.0.0.1:52437/v/coolify-api)
|
|
130
|
+
curl -s -H "Authorization: Bearer $_COOLIFY" https://deploy.regendevcorp.com/api/v1/applications
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
If clauth daemon is not responding:
|
|
134
|
+
```
|
|
135
|
+
BLOCKED: clauth daemon not responding. Run scripts\restart-clauth.bat, unlock at http://127.0.0.1:52437
|
|
136
|
+
```
|
|
137
|
+
|
|
123
138
|
## References
|
|
124
139
|
|
|
125
140
|
- Type-specific checklists + DNS tree + gate commands: `docs/runbooks/coolify-deploy-checklist.md`
|
|
126
141
|
- Rules / registry RPCs / hard limits: `.claude/rules/coolify-deployment.md`
|
|
127
|
-
- MCP server: `@masonator/coolify-mcp` (38 tools)
|
|
128
142
|
- Infrastructure constants:
|
|
129
143
|
```
|
|
130
144
|
Server UUID: ih386anenvvvn6fy1umtyow0
|
package/package.json
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Bad Infrastructure Guide — FIXTURE (contains banned terms for self-test)
|
|
2
|
+
> This file is used by the rdc:self-test guide-content validator fixture test.
|
|
3
|
+
> It intentionally contains banned terms in positive-instruction context.
|
|
4
|
+
|
|
5
|
+
## Coolify Access
|
|
6
|
+
|
|
7
|
+
All Coolify operations via `@masonator/coolify-mcp` (38 tools, pre-authenticated).
|
|
8
|
+
|
|
9
|
+
Use the `@masonator/coolify-mcp` MCP tool to deploy apps.
|
|
10
|
+
|
|
11
|
+
You can also use brand-studio for design token work via @regen/brand-studio.
|
|
12
|
+
|
|
13
|
+
## Clauth Key
|
|
14
|
+
|
|
15
|
+
Use `curl -s http://127.0.0.1:52437/v/nonexistent-key` for that service.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# Good Infrastructure Guide — FIXTURE (clean, no banned terms)
|
|
2
|
+
> This file is used by the rdc:self-test guide-content validator fixture test.
|
|
3
|
+
> It is a clean guide with no banned terms.
|
|
4
|
+
|
|
5
|
+
## Coolify Access
|
|
6
|
+
|
|
7
|
+
There is no Coolify MCP server. Do not reference `@masonator/coolify-mcp`.
|
|
8
|
+
Never use brand-studio — the canonical name is Studio.
|
|
9
|
+
The @regen/brand-studio package does not exist — use @regen/studio.
|
|
10
|
+
|
|
11
|
+
All Coolify operations use the clauth daemon + REST API:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
_COOLIFY=$(curl -s http://127.0.0.1:52437/v/coolify-api)
|
|
15
|
+
curl -s -H "Authorization: Bearer $_COOLIFY" https://deploy.regendevcorp.com/api/v1/applications
|
|
16
|
+
```
|
package/scripts/self-test.mjs
CHANGED
|
@@ -49,6 +49,181 @@ const HOOKS_DIR = join(REPO_ROOT, "hooks");
|
|
|
49
49
|
const PLUGIN_MANIFEST = join(REPO_ROOT, ".claude-plugin", "plugin.json");
|
|
50
50
|
const PACKAGE_JSON = join(REPO_ROOT, "package.json");
|
|
51
51
|
|
|
52
|
+
// ─── Guide-content validator constants ───────────────────────────────────────
|
|
53
|
+
// Path to the regen-root project (sibling of rdc-skills).
|
|
54
|
+
// Override with REGEN_ROOT env var when running from a non-standard location.
|
|
55
|
+
const REGEN_ROOT = process.env.REGEN_ROOT || "C:/Dev/regen-root";
|
|
56
|
+
|
|
57
|
+
// Terms that must NOT appear in guide/rule files as positive instructions.
|
|
58
|
+
// A line is flagged only when it does NOT contain a known negation pattern.
|
|
59
|
+
const GUIDE_BANNED_TERMS = [
|
|
60
|
+
"@masonator/coolify-mcp",
|
|
61
|
+
"@masonator",
|
|
62
|
+
"coolify-mcp",
|
|
63
|
+
"@regen/brand-studio",
|
|
64
|
+
"brand-studio",
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
// Negation patterns — if a line contains one of these, the banned-term
|
|
68
|
+
// occurrence is an explicit "don't use" warning and should NOT be flagged.
|
|
69
|
+
const GUIDE_NEGATION_PATTERNS = [
|
|
70
|
+
/\bdo not\b/i,
|
|
71
|
+
/\bnever\b/i,
|
|
72
|
+
/\bno such\b/i,
|
|
73
|
+
/\bdoes not exist\b/i,
|
|
74
|
+
/\bbanned\b/i,
|
|
75
|
+
/\bnot reference\b/i,
|
|
76
|
+
/\bnot use\b/i,
|
|
77
|
+
/\bavoid\b/i,
|
|
78
|
+
/\bremoved\b/i,
|
|
79
|
+
/\bdeprecated\b/i,
|
|
80
|
+
// Markdown table row showing a WRONG→CORRECT mapping (naming-corrections.md pattern)
|
|
81
|
+
/^\|[^|]*WRONG[^|]*\|/i,
|
|
82
|
+
// A table row where the term is in the WRONG column (first data column after the | WRONG | header)
|
|
83
|
+
// Heuristic: line starts with | and the term appears before the first CORRECT value
|
|
84
|
+
/^\|\s*(Brand Studio|brand-studio|@regen\/brand-studio|@masonator[^ |]*|coolify-mcp)[^|]*\|\s*\*\*/,
|
|
85
|
+
];
|
|
86
|
+
|
|
87
|
+
// Known valid clauth key names. Extend when new keys are added to the vault.
|
|
88
|
+
// Source: C:/Dev/regen-root/.rdc/guides/agent-bootstrap.md credential table
|
|
89
|
+
// + C:/Dev/regen-root/.claude/rules/clauth-endpoints.md
|
|
90
|
+
const KNOWN_CLAUTH_KEYS = new Set([
|
|
91
|
+
"coolify-api",
|
|
92
|
+
"cloudflare",
|
|
93
|
+
"npm",
|
|
94
|
+
"supabase",
|
|
95
|
+
"supabase-anon",
|
|
96
|
+
"supabase-db",
|
|
97
|
+
"r2-access-key-id",
|
|
98
|
+
"r2-secret-key",
|
|
99
|
+
"anthropic",
|
|
100
|
+
"openai",
|
|
101
|
+
"github",
|
|
102
|
+
"github-token",
|
|
103
|
+
"vultr-dev-ssh",
|
|
104
|
+
"mcp-web-research-secret",
|
|
105
|
+
"mcp-regen-media-secret",
|
|
106
|
+
"web-research",
|
|
107
|
+
"regen-media",
|
|
108
|
+
]);
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* Audit a single guide or rule file for banned terms and clauth key validity.
|
|
112
|
+
*
|
|
113
|
+
* Returns an array of finding objects:
|
|
114
|
+
* { level: 'error'|'warn', code: string, file: string, line: number, message: string }
|
|
115
|
+
*/
|
|
116
|
+
function auditGuideFile(filepath, relPath) {
|
|
117
|
+
const findings = [];
|
|
118
|
+
let text;
|
|
119
|
+
try {
|
|
120
|
+
text = readFileSync(filepath, "utf8");
|
|
121
|
+
} catch (e) {
|
|
122
|
+
findings.push({ level: "error", code: "guide-unreadable", file: relPath, line: 0, message: `cannot read: ${e.message}` });
|
|
123
|
+
return findings;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
const lines = text.split(/\r?\n/);
|
|
127
|
+
lines.forEach((line, i) => {
|
|
128
|
+
const lineNo = i + 1;
|
|
129
|
+
|
|
130
|
+
// Check banned terms (skip negation lines and comment lines)
|
|
131
|
+
for (const term of GUIDE_BANNED_TERMS) {
|
|
132
|
+
if (line.includes(term)) {
|
|
133
|
+
const isNegated = GUIDE_NEGATION_PATTERNS.some((re) => re.test(line));
|
|
134
|
+
if (!isNegated) {
|
|
135
|
+
findings.push({
|
|
136
|
+
level: "error",
|
|
137
|
+
code: "guide-banned-term",
|
|
138
|
+
file: relPath,
|
|
139
|
+
line: lineNo,
|
|
140
|
+
message: `banned term "${term}" found as positive instruction — replace with clauth+REST pattern`,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
// Check clauth /v/<key> references — flag unknown key names
|
|
147
|
+
// Pattern: /v/some-key-name (standalone path, not inside a larger URL path)
|
|
148
|
+
const clauthKeyRe = /\bhttp:\/\/127\.0\.0\.1:52437\/v\/([\w-]+)/g;
|
|
149
|
+
let m;
|
|
150
|
+
while ((m = clauthKeyRe.exec(line)) !== null) {
|
|
151
|
+
const key = m[1];
|
|
152
|
+
if (!KNOWN_CLAUTH_KEYS.has(key)) {
|
|
153
|
+
findings.push({
|
|
154
|
+
level: "warn",
|
|
155
|
+
code: "guide-clauth-key-unknown",
|
|
156
|
+
file: relPath,
|
|
157
|
+
line: lineNo,
|
|
158
|
+
message: `clauth key "${key}" not in known key list — verify it exists in the vault`,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
return findings;
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Run the guide-content validator across:
|
|
169
|
+
* 1. C:/Dev/rdc-skills/guides/**\/*.md (base guides shipped with the plugin)
|
|
170
|
+
* 2. REGEN_ROOT/.rdc/guides/**\/*.md (project-level agent guides)
|
|
171
|
+
* 3. REGEN_ROOT/.claude/rules/**\/*.md (project auto-loaded rules)
|
|
172
|
+
*
|
|
173
|
+
* Also scans fixture guides under scripts/fixtures/guides/ when GUIDE_FIXTURE_DIR is set
|
|
174
|
+
* (used by the self-test's own test suite).
|
|
175
|
+
*
|
|
176
|
+
* Returns { findings[], ok, stats }
|
|
177
|
+
*/
|
|
178
|
+
function runGuideContentValidator({ fixtureDir } = {}) {
|
|
179
|
+
const allFindings = [];
|
|
180
|
+
let filesScanned = 0;
|
|
181
|
+
|
|
182
|
+
function scanDir(dir, prefix) {
|
|
183
|
+
if (!existsSync(dir)) return;
|
|
184
|
+
let entries;
|
|
185
|
+
try { entries = readdirSync(dir); } catch { return; }
|
|
186
|
+
for (const entry of entries) {
|
|
187
|
+
const fullPath = join(dir, entry);
|
|
188
|
+
let stat;
|
|
189
|
+
try { stat = statSync(fullPath); } catch { continue; }
|
|
190
|
+
if (stat.isDirectory()) {
|
|
191
|
+
scanDir(fullPath, `${prefix}/${entry}`);
|
|
192
|
+
} else if (entry.endsWith(".md")) {
|
|
193
|
+
const relPath = `${prefix}/${entry}`;
|
|
194
|
+
const findings = auditGuideFile(fullPath, relPath);
|
|
195
|
+
allFindings.push(...findings);
|
|
196
|
+
filesScanned++;
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
// 1. Base guides in rdc-skills repo
|
|
202
|
+
scanDir(GUIDES_DIR, "guides");
|
|
203
|
+
|
|
204
|
+
// 2. Regen-root agent guides
|
|
205
|
+
scanDir(join(REGEN_ROOT, ".rdc", "guides"), "regen-root/.rdc/guides");
|
|
206
|
+
|
|
207
|
+
// 3. Regen-root auto-loaded rules
|
|
208
|
+
scanDir(join(REGEN_ROOT, ".claude", "rules"), "regen-root/.claude/rules");
|
|
209
|
+
|
|
210
|
+
// 4. Fixture directory (for self-test's own test assertions)
|
|
211
|
+
if (fixtureDir && existsSync(fixtureDir)) {
|
|
212
|
+
scanDir(fixtureDir, "fixtures");
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const errors = allFindings.filter((f) => f.level === "error");
|
|
216
|
+
const warnings = allFindings.filter((f) => f.level === "warn");
|
|
217
|
+
|
|
218
|
+
return {
|
|
219
|
+
findings: allFindings,
|
|
220
|
+
errors,
|
|
221
|
+
warnings,
|
|
222
|
+
ok: errors.length === 0,
|
|
223
|
+
stats: { files_scanned: filesScanned, errors: errors.length, warnings: warnings.length },
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
52
227
|
const STANDARD_BANNER = [
|
|
53
228
|
"> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`",
|
|
54
229
|
"> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.",
|
|
@@ -775,6 +950,8 @@ function main() {
|
|
|
775
950
|
agentResults = agentFiles.map((f) => auditAgentGuide(join(AGENT_GUIDES_DIR, f)));
|
|
776
951
|
}
|
|
777
952
|
|
|
953
|
+
const guideValidatorResultJson = !ONLY_SKILL ? runGuideContentValidator() : null;
|
|
954
|
+
|
|
778
955
|
const failed = results.filter((r) => r.errors.length > 0);
|
|
779
956
|
const warned = results.filter((r) => r.warnings.length > 0 && r.errors.length === 0);
|
|
780
957
|
const clean = results.filter((r) => r.errors.length === 0 && r.warnings.length === 0);
|
|
@@ -785,11 +962,13 @@ function main() {
|
|
|
785
962
|
...manifestAudit.findings.filter((f) => f.level === "error"),
|
|
786
963
|
...duplicateFindings.filter((f) => f.level === "error"),
|
|
787
964
|
...orphanHookFindings.filter((f) => f.level === "error"),
|
|
965
|
+
...(guideValidatorResultJson ? guideValidatorResultJson.errors : []),
|
|
788
966
|
];
|
|
789
967
|
const globalWarnings = [
|
|
790
968
|
...manifestAudit.findings.filter((f) => f.level === "warn"),
|
|
791
969
|
...duplicateFindings.filter((f) => f.level === "warn"),
|
|
792
970
|
...orphanHookFindings.filter((f) => f.level === "warn"),
|
|
971
|
+
...(guideValidatorResultJson ? guideValidatorResultJson.warnings : []),
|
|
793
972
|
];
|
|
794
973
|
const fail =
|
|
795
974
|
failed.length > 0 ||
|
|
@@ -819,6 +998,13 @@ function main() {
|
|
|
819
998
|
findings: manifestAudit.findings,
|
|
820
999
|
},
|
|
821
1000
|
global_findings: [...duplicateFindings, ...orphanHookFindings],
|
|
1001
|
+
guide_content_validator: guideValidatorResultJson
|
|
1002
|
+
? {
|
|
1003
|
+
ok: guideValidatorResultJson.ok,
|
|
1004
|
+
stats: guideValidatorResultJson.stats,
|
|
1005
|
+
findings: guideValidatorResultJson.findings,
|
|
1006
|
+
}
|
|
1007
|
+
: null,
|
|
822
1008
|
results: results.map((r) => ({
|
|
823
1009
|
skill: r.skill,
|
|
824
1010
|
file: r.file,
|
|
@@ -918,6 +1104,28 @@ function main() {
|
|
|
918
1104
|
}
|
|
919
1105
|
}
|
|
920
1106
|
|
|
1107
|
+
// Guide-content validation — check .rdc/guides + .claude/rules for banned terms and invalid clauth keys
|
|
1108
|
+
let guideValidatorResult = null;
|
|
1109
|
+
if (!ONLY_SKILL) {
|
|
1110
|
+
guideValidatorResult = runGuideContentValidator();
|
|
1111
|
+
if (guideValidatorResult.stats.files_scanned > 0) {
|
|
1112
|
+
console.log(`\nguide-content validator (${guideValidatorResult.stats.files_scanned} files)\n`);
|
|
1113
|
+
console.log("─".repeat(80));
|
|
1114
|
+
if (guideValidatorResult.findings.length === 0) {
|
|
1115
|
+
console.log(" ✓ no banned terms or invalid clauth keys found");
|
|
1116
|
+
} else {
|
|
1117
|
+
for (const f of guideValidatorResult.findings) {
|
|
1118
|
+
const tag = f.level === "error" ? "FAIL" : "WARN";
|
|
1119
|
+
console.log(` [${tag}] ${f.file}:${f.line} ${f.code} ${f.message}`);
|
|
1120
|
+
}
|
|
1121
|
+
}
|
|
1122
|
+
console.log("─".repeat(80));
|
|
1123
|
+
const gErrors = guideValidatorResult.errors.length;
|
|
1124
|
+
const gWarnings = guideValidatorResult.warnings.length;
|
|
1125
|
+
console.log(`total: ${guideValidatorResult.stats.files_scanned} | errors: ${gErrors} | warnings: ${gWarnings}`);
|
|
1126
|
+
}
|
|
1127
|
+
}
|
|
1128
|
+
|
|
921
1129
|
// Cross-skill checks — deferred until all audits complete
|
|
922
1130
|
let duplicateFindings = [];
|
|
923
1131
|
let orphanHookFindings = [];
|
|
@@ -944,11 +1152,13 @@ function main() {
|
|
|
944
1152
|
...manifestAudit.findings.filter((f) => f.level === "error"),
|
|
945
1153
|
...duplicateFindings.filter((f) => f.level === "error"),
|
|
946
1154
|
...orphanHookFindings.filter((f) => f.level === "error"),
|
|
1155
|
+
...(guideValidatorResult ? guideValidatorResult.errors : []),
|
|
947
1156
|
];
|
|
948
1157
|
const globalWarnings = [
|
|
949
1158
|
...manifestAudit.findings.filter((f) => f.level === "warn"),
|
|
950
1159
|
...duplicateFindings.filter((f) => f.level === "warn"),
|
|
951
1160
|
...orphanHookFindings.filter((f) => f.level === "warn"),
|
|
1161
|
+
...(guideValidatorResult ? guideValidatorResult.warnings : []),
|
|
952
1162
|
];
|
|
953
1163
|
|
|
954
1164
|
const fail =
|
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// test-guide-validator.mjs — Unit test for the guide-content validator
|
|
3
|
+
//
|
|
4
|
+
// Proves:
|
|
5
|
+
// 1. The validator flags guides containing banned terms in positive-instruction context
|
|
6
|
+
// 2. The validator passes guides that mention banned terms only as warnings/negations
|
|
7
|
+
// 3. The validator flags unknown clauth key names
|
|
8
|
+
// 4. The validator passes known clauth key names
|
|
9
|
+
//
|
|
10
|
+
// Usage:
|
|
11
|
+
// node scripts/test-guide-validator.mjs
|
|
12
|
+
//
|
|
13
|
+
// Exit codes:
|
|
14
|
+
// 0 = all assertions pass
|
|
15
|
+
// 1 = one or more assertions failed
|
|
16
|
+
|
|
17
|
+
import { dirname, join, resolve } from "node:path";
|
|
18
|
+
import { fileURLToPath } from "node:url";
|
|
19
|
+
|
|
20
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const REPO_ROOT = resolve(__dirname, "..");
|
|
22
|
+
|
|
23
|
+
// Import the validator functions from self-test.mjs by running it in test mode.
|
|
24
|
+
// Since self-test.mjs is not a module that exports cleanly, we replicate the
|
|
25
|
+
// validator logic here using the same constants and algorithms.
|
|
26
|
+
// (The shared implementation lives in runGuideContentValidator / auditGuideFile.)
|
|
27
|
+
//
|
|
28
|
+
// We import via dynamic import of the self-test module's exported helpers —
|
|
29
|
+
// but since self-test.mjs has no exports (it runs immediately), we inline
|
|
30
|
+
// the equivalent test using the same fixture files it targets.
|
|
31
|
+
|
|
32
|
+
// ─── Inline validator (mirrors self-test.mjs logic) ────────────────────────
|
|
33
|
+
import { readFileSync, existsSync, readdirSync, statSync } from "node:fs";
|
|
34
|
+
|
|
35
|
+
const GUIDE_BANNED_TERMS = [
|
|
36
|
+
"@masonator/coolify-mcp",
|
|
37
|
+
"@masonator",
|
|
38
|
+
"coolify-mcp",
|
|
39
|
+
"@regen/brand-studio",
|
|
40
|
+
"brand-studio",
|
|
41
|
+
];
|
|
42
|
+
|
|
43
|
+
const GUIDE_NEGATION_PATTERNS = [
|
|
44
|
+
/\bdo not\b/i,
|
|
45
|
+
/\bnever\b/i,
|
|
46
|
+
/\bno such\b/i,
|
|
47
|
+
/\bdoes not exist\b/i,
|
|
48
|
+
/\bbanned\b/i,
|
|
49
|
+
/\bnot reference\b/i,
|
|
50
|
+
/\bnot use\b/i,
|
|
51
|
+
/\bavoid\b/i,
|
|
52
|
+
/\bremoved\b/i,
|
|
53
|
+
/\bdeprecated\b/i,
|
|
54
|
+
// Markdown table row showing a WRONG→CORRECT mapping (naming-corrections.md pattern)
|
|
55
|
+
/^\|[^|]*WRONG[^|]*\|/i,
|
|
56
|
+
// A table row where the term is in the WRONG column (first data column after the | WRONG | header)
|
|
57
|
+
/^\|\s*(Brand Studio|brand-studio|@regen\/brand-studio|@masonator[^ |]*|coolify-mcp)[^|]*\|\s*\*\*/,
|
|
58
|
+
];
|
|
59
|
+
|
|
60
|
+
const KNOWN_CLAUTH_KEYS = new Set([
|
|
61
|
+
"coolify-api",
|
|
62
|
+
"cloudflare",
|
|
63
|
+
"npm",
|
|
64
|
+
"supabase",
|
|
65
|
+
"supabase-anon",
|
|
66
|
+
"supabase-db",
|
|
67
|
+
"r2-access-key-id",
|
|
68
|
+
"r2-secret-key",
|
|
69
|
+
"anthropic",
|
|
70
|
+
"openai",
|
|
71
|
+
"github",
|
|
72
|
+
"github-token",
|
|
73
|
+
"vultr-dev-ssh",
|
|
74
|
+
"mcp-web-research-secret",
|
|
75
|
+
"mcp-regen-media-secret",
|
|
76
|
+
"web-research",
|
|
77
|
+
"regen-media",
|
|
78
|
+
]);
|
|
79
|
+
|
|
80
|
+
function auditGuideFile(filepath, relPath) {
|
|
81
|
+
const findings = [];
|
|
82
|
+
let text;
|
|
83
|
+
try {
|
|
84
|
+
text = readFileSync(filepath, "utf8");
|
|
85
|
+
} catch (e) {
|
|
86
|
+
findings.push({ level: "error", code: "guide-unreadable", file: relPath, line: 0, message: `cannot read: ${e.message}` });
|
|
87
|
+
return findings;
|
|
88
|
+
}
|
|
89
|
+
const lines = text.split(/\r?\n/);
|
|
90
|
+
lines.forEach((line, i) => {
|
|
91
|
+
const lineNo = i + 1;
|
|
92
|
+
for (const term of GUIDE_BANNED_TERMS) {
|
|
93
|
+
if (line.includes(term)) {
|
|
94
|
+
const isNegated = GUIDE_NEGATION_PATTERNS.some((re) => re.test(line));
|
|
95
|
+
if (!isNegated) {
|
|
96
|
+
findings.push({ level: "error", code: "guide-banned-term", file: relPath, line: lineNo, message: `banned term "${term}"` });
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const clauthKeyRe = /\bhttp:\/\/127\.0\.0\.1:52437\/v\/([\w-]+)/g;
|
|
101
|
+
let m;
|
|
102
|
+
while ((m = clauthKeyRe.exec(line)) !== null) {
|
|
103
|
+
const key = m[1];
|
|
104
|
+
if (!KNOWN_CLAUTH_KEYS.has(key)) {
|
|
105
|
+
findings.push({ level: "warn", code: "guide-clauth-key-unknown", file: relPath, line: lineNo, message: `unknown clauth key "${key}"` });
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
return findings;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function scanDir(dir, prefix) {
|
|
113
|
+
const findings = [];
|
|
114
|
+
if (!existsSync(dir)) return findings;
|
|
115
|
+
let entries;
|
|
116
|
+
try { entries = readdirSync(dir); } catch { return findings; }
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
const fullPath = join(dir, entry);
|
|
119
|
+
let stat;
|
|
120
|
+
try { stat = statSync(fullPath); } catch { continue; }
|
|
121
|
+
if (stat.isDirectory()) {
|
|
122
|
+
findings.push(...scanDir(fullPath, `${prefix}/${entry}`));
|
|
123
|
+
} else if (entry.endsWith(".md")) {
|
|
124
|
+
findings.push(...auditGuideFile(fullPath, `${prefix}/${entry}`));
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
return findings;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── Test runner ─────────────────────────────────────────────────────────────
|
|
131
|
+
|
|
132
|
+
let passed = 0;
|
|
133
|
+
let failed = 0;
|
|
134
|
+
|
|
135
|
+
function assert(description, condition, detail = "") {
|
|
136
|
+
if (condition) {
|
|
137
|
+
console.log(` ✓ ${description}`);
|
|
138
|
+
passed++;
|
|
139
|
+
} else {
|
|
140
|
+
console.error(` ✗ FAIL: ${description}${detail ? ` — ${detail}` : ""}`);
|
|
141
|
+
failed++;
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
console.log("\nrdc:self-test — guide-content validator unit tests\n");
|
|
146
|
+
|
|
147
|
+
// ─── Test 1: bad-guide.md should produce errors for banned terms ─────────────
|
|
148
|
+
console.log("Test 1: bad-guide.md flags banned terms");
|
|
149
|
+
const BAD_FIXTURE = join(REPO_ROOT, "scripts/fixtures/guides/bad-guide.md");
|
|
150
|
+
const badFindings = auditGuideFile(BAD_FIXTURE, "fixtures/guides/bad-guide.md");
|
|
151
|
+
const badErrors = badFindings.filter((f) => f.level === "error" && f.code === "guide-banned-term");
|
|
152
|
+
assert("bad-guide.md produces at least 2 banned-term errors", badErrors.length >= 2, `got ${badErrors.length}`);
|
|
153
|
+
assert("flags @masonator/coolify-mcp", badErrors.some((f) => f.message.includes("@masonator/coolify-mcp")));
|
|
154
|
+
assert("flags brand-studio or @regen/brand-studio", badErrors.some((f) => f.message.includes("brand-studio")));
|
|
155
|
+
|
|
156
|
+
// ─── Test 2: bad-guide.md flags unknown clauth key ───────────────────────────
|
|
157
|
+
console.log("\nTest 2: bad-guide.md flags unknown clauth key");
|
|
158
|
+
const badKeyWarnings = badFindings.filter((f) => f.code === "guide-clauth-key-unknown");
|
|
159
|
+
assert("bad-guide.md warns on unknown clauth key 'nonexistent-key'", badKeyWarnings.length >= 1, `got ${badKeyWarnings.length}`);
|
|
160
|
+
assert("warning message references nonexistent-key", badKeyWarnings.some((f) => f.message.includes("nonexistent-key")));
|
|
161
|
+
|
|
162
|
+
// ─── Test 3: good-guide.md produces no errors (negated mentions are OK) ──────
|
|
163
|
+
console.log("\nTest 3: good-guide.md passes (negated mentions are not flagged)");
|
|
164
|
+
const GOOD_FIXTURE = join(REPO_ROOT, "scripts/fixtures/guides-clean/good-guide.md");
|
|
165
|
+
const goodFindings = auditGuideFile(GOOD_FIXTURE, "fixtures/guides-clean/good-guide.md");
|
|
166
|
+
const goodErrors = goodFindings.filter((f) => f.level === "error");
|
|
167
|
+
assert("good-guide.md produces 0 banned-term errors", goodErrors.length === 0, `got ${goodErrors.length}: ${goodErrors.map((f) => f.message).join("; ")}`);
|
|
168
|
+
|
|
169
|
+
// good-guide uses coolify-api which is a known key — no key warnings
|
|
170
|
+
const goodKeyWarnings = goodFindings.filter((f) => f.code === "guide-clauth-key-unknown");
|
|
171
|
+
assert("good-guide.md produces 0 unknown-key warnings", goodKeyWarnings.length === 0, `got ${goodKeyWarnings.length}`);
|
|
172
|
+
|
|
173
|
+
// ─── Test 4: scan fixture directory returns combined findings ─────────────────
|
|
174
|
+
console.log("\nTest 4: scanDir finds findings across fixture dirs");
|
|
175
|
+
const BAD_DIR = join(REPO_ROOT, "scripts/fixtures/guides");
|
|
176
|
+
const dirFindings = scanDir(BAD_DIR, "fixtures");
|
|
177
|
+
const dirErrors = dirFindings.filter((f) => f.level === "error");
|
|
178
|
+
assert("scanDir on bad fixture dir returns errors", dirErrors.length >= 2, `got ${dirErrors.length}`);
|
|
179
|
+
|
|
180
|
+
const GOOD_DIR = join(REPO_ROOT, "scripts/fixtures/guides-clean");
|
|
181
|
+
const cleanFindings = scanDir(GOOD_DIR, "fixtures");
|
|
182
|
+
const cleanErrors = cleanFindings.filter((f) => f.level === "error");
|
|
183
|
+
assert("scanDir on clean fixture dir returns 0 errors", cleanErrors.length === 0, `got ${cleanErrors.length}`);
|
|
184
|
+
|
|
185
|
+
// ─── Summary ─────────────────────────────────────────────────────────────────
|
|
186
|
+
console.log(`\n${"─".repeat(60)}`);
|
|
187
|
+
console.log(`guide-validator tests: ${passed} passed, ${failed} failed`);
|
|
188
|
+
if (failed > 0) {
|
|
189
|
+
console.error(`\n❌ FAIL — ${failed} assertion(s) did not pass`);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
} else {
|
|
192
|
+
console.log(`\n✓ PASS — all guide-content validator assertions passed`);
|
|
193
|
+
process.exit(0);
|
|
194
|
+
}
|
package/skills/deploy/SKILL.md
CHANGED
|
@@ -134,11 +134,57 @@ Severity rules:
|
|
|
134
134
|
|
|
135
135
|
`--fix` auto-remediates only: missing watch_paths, registry row updates, CF cache purges. Never touches env vars, DNS, or container config without explicit confirmation.
|
|
136
136
|
|
|
137
|
+
## Coolify Access — clauth + REST API
|
|
138
|
+
|
|
139
|
+
All Coolify operations use the clauth daemon and the Coolify REST API directly.
|
|
140
|
+
There is no Coolify MCP server. Do not reference `@masonator/coolify-mcp`.
|
|
141
|
+
|
|
142
|
+
```bash
|
|
143
|
+
# Get token (plain text — no JSON parsing needed)
|
|
144
|
+
_COOLIFY=$(curl -s http://127.0.0.1:52437/v/coolify-api)
|
|
145
|
+
|
|
146
|
+
# List applications
|
|
147
|
+
curl -s -H "Authorization: Bearer $_COOLIFY" \
|
|
148
|
+
https://deploy.regendevcorp.com/api/v1/applications
|
|
149
|
+
|
|
150
|
+
# Get application details
|
|
151
|
+
curl -s -H "Authorization: Bearer $_COOLIFY" \
|
|
152
|
+
https://deploy.regendevcorp.com/api/v1/applications/<uuid>
|
|
153
|
+
|
|
154
|
+
# Deploy (trigger)
|
|
155
|
+
curl -s -X POST -H "Authorization: Bearer $_COOLIFY" \
|
|
156
|
+
https://deploy.regendevcorp.com/api/v1/applications/<uuid>/deploy
|
|
157
|
+
|
|
158
|
+
# Get deployment logs
|
|
159
|
+
curl -s -H "Authorization: Bearer $_COOLIFY" \
|
|
160
|
+
https://deploy.regendevcorp.com/api/v1/deployments/<deployment-id>
|
|
161
|
+
|
|
162
|
+
# Set env var
|
|
163
|
+
curl -s -X POST -H "Authorization: Bearer $_COOLIFY" \
|
|
164
|
+
-H "Content-Type: application/json" \
|
|
165
|
+
-d '{"key":"<KEY>","value":"<VALUE>"}' \
|
|
166
|
+
https://deploy.regendevcorp.com/api/v1/applications/<uuid>/envs
|
|
167
|
+
|
|
168
|
+
# Set watch_paths
|
|
169
|
+
curl -s -X PATCH -H "Authorization: Bearer $_COOLIFY" \
|
|
170
|
+
-H "Content-Type: application/json" \
|
|
171
|
+
-d '{"watch_paths":"apps/<name>/**\npackages/**"}' \
|
|
172
|
+
https://deploy.regendevcorp.com/api/v1/applications/<uuid>
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
**Never print `$_COOLIFY` to stdout.** Inline from clauth only — do not assign raw strings.
|
|
176
|
+
|
|
177
|
+
If clauth daemon is not responding (`curl -s http://127.0.0.1:52437/ping` fails):
|
|
178
|
+
```
|
|
179
|
+
BLOCKED: clauth daemon is not responding.
|
|
180
|
+
Fix: Run C:\Dev\regen-root\scripts\restart-clauth.bat, then unlock at http://127.0.0.1:52437
|
|
181
|
+
I cannot proceed until this is resolved.
|
|
182
|
+
```
|
|
183
|
+
|
|
137
184
|
## References
|
|
138
185
|
|
|
139
186
|
- Type-specific checklists + DNS tree + gate commands: `docs/runbooks/coolify-deploy-checklist.md`
|
|
140
187
|
- Rules / registry RPCs / hard limits: `.claude/context/coolify-deployment.md`
|
|
141
|
-
- MCP server: `@masonator/coolify-mcp` (38 tools)
|
|
142
188
|
- Infrastructure constants:
|
|
143
189
|
```
|
|
144
190
|
Server UUID: ih386anenvvvn6fy1umtyow0
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
---
|
|
2
|
-
name: terminal-config
|
|
3
|
-
description: "
|
|
2
|
+
name: rdc:terminal-config
|
|
3
|
+
description: "Usage `rdc:terminal-config <task>` — read and safely modify Windows Terminal settings and cell startup sequencing. Contains canonical file locations, profile GUIDs, keybinding map, and what NEVER to change."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
> **⚠️ OUTPUT CONTRACT (READ FIRST):** `guides/output-contract.md`
|
|
7
7
|
> Checklist-only output. No tool-call narration. No raw MCP/JSON/log dumps.
|
|
8
8
|
> One checklist upfront, updated in place, shown again at end with a 1-line verdict.
|
|
9
9
|
|
|
10
|
-
# terminal-config — Windows Terminal & Cell Startup Reference
|
|
10
|
+
# rdc:terminal-config — Windows Terminal & Cell Startup Reference
|
|
11
11
|
|
|
12
12
|
## When to Use
|
|
13
13
|
- Before modifying any terminal setting, profile, or keybinding
|
|
@@ -31,7 +31,7 @@ description: "Read and safely modify Windows Terminal settings and cell startup
|
|
|
31
31
|
| File | Path | Purpose |
|
|
32
32
|
|------|------|---------|
|
|
33
33
|
| **Terminal settings** | `C:\Users\DaveLadouceur\AppData\Local\Packages\Microsoft.WindowsTerminal_8wekyb3d8bbwe\LocalState\settings.json` | Main config — profiles, keybindings, color schemes |
|
|
34
|
-
| **Cell init script** | `C:\Dev\regen-root\scripts\cell-init.
|
|
34
|
+
| **Cell init script** | `C:\Dev\regen-root\scripts\cell-init.ps1` | Launched by each cell profile (PowerShell 7). Sets CELL_ROLE, prints banner, runs `claude --append-system-prompt` |
|
|
35
35
|
| **Cell state dir** | `C:\Dev\regen-root\.cell-state\` | PID lockfiles per cell (`sv.lock`, `cell-portal.lock`, etc.) |
|
|
36
36
|
| **Claude keybindings** | `C:\Users\DaveLadouceur\.claude\keybindings.json` | Claude Code keybindings (separate from Terminal) |
|
|
37
37
|
| **Claude settings** | `C:\Dev\regen-root\.claude\settings.json` | Project-level Claude Code settings, hooks, permissions |
|
|
@@ -46,9 +46,9 @@ Every profile has a fixed GUID. Do not change them.
|
|
|
46
46
|
| GUID | Name | Tab Color | Purpose |
|
|
47
47
|
|------|------|-----------|---------|
|
|
48
48
|
| `{574e775e-4f2a-5b96-ac1e-a2962a402336}` | PowerShell | — | **Default profile** — PowerShell 7 Core |
|
|
49
|
-
| `{e7c1128b-e51f-4eba-bcdb-85f550c31b97}` | SV | `#1A6B3C` (green) | Supervisor cell — full repo access, runs `cell-init.
|
|
50
|
-
| `{190a2f39-f6ad-4638-8fef-e4c7aa58c349}` | Claude | `#B91C1C` (red) | Claude conversation cell, runs `cell-init.
|
|
51
|
-
| `{2d0248ef-7bb7-4b9e-b381-c4aa1f570f49}` | Codex | `#0F766E` (teal) | Codex AI cell — runs `
|
|
49
|
+
| `{e7c1128b-e51f-4eba-bcdb-85f550c31b97}` | SV | `#1A6B3C` (green) | Supervisor cell — full repo access, runs `cell-init.ps1 sv` |
|
|
50
|
+
| `{190a2f39-f6ad-4638-8fef-e4c7aa58c349}` | Claude | `#B91C1C` (red) | Claude conversation cell, runs `cell-init.ps1 sv` |
|
|
51
|
+
| `{2d0248ef-7bb7-4b9e-b381-c4aa1f570f49}` | Codex | `#0F766E` (teal) | Codex AI cell — runs `pwsh.exe -NoLogo -NoExit -Command codex` |
|
|
52
52
|
| `{767642b0-606c-468c-89a0-4573df6fcdaf}` | VS Code | `#2D5986` (blue) | Launches `code-safe C:\Dev\regen-root` |
|
|
53
53
|
| `{6cbd327a-5b5e-4e83-96f6-041615382d36}` | PowerShell (Admin) | `#1E3A5F` (dark blue) | Elevated PowerShell — uses `LIFEAI Slate` color scheme |
|
|
54
54
|
| `{0caa0dad-35be-5f56-a8ff-afceeeaa6101}` | Command Prompt | — | Legacy CMD — visible but rarely used |
|
|
@@ -57,13 +57,14 @@ Every profile has a fixed GUID. Do not change them.
|
|
|
57
57
|
### Profile commandlines
|
|
58
58
|
|
|
59
59
|
```
|
|
60
|
-
SV / Claude:
|
|
61
|
-
Codex:
|
|
60
|
+
SV / Claude: pwsh.exe -NoLogo -NoExit -ExecutionPolicy Bypass -File "C:\Dev\regen-root\scripts\cell-init.ps1" sv
|
|
61
|
+
Codex: pwsh.exe -NoLogo -NoExit -Command codex
|
|
62
62
|
VS Code: cmd.exe /c start "" code-safe C:\Dev\regen-root
|
|
63
63
|
Admin PS: powershell.exe -NoLogo (+ elevate: true)
|
|
64
64
|
```
|
|
65
65
|
|
|
66
66
|
All cell profiles set `"startingDirectory": "C:\\Dev\\regen-root"`.
|
|
67
|
+
SV / Claude / Codex all run on **PowerShell 7 (`pwsh.exe`)** — never bare `powershell.exe` (that resolves to legacy Windows PowerShell 5.1).
|
|
67
68
|
|
|
68
69
|
---
|
|
69
70
|
|
|
@@ -140,15 +141,14 @@ Font defaults (in `profiles.defaults`):
|
|
|
140
141
|
|
|
141
142
|
## Cell Startup Sequencing
|
|
142
143
|
|
|
143
|
-
`cell-init.
|
|
144
|
+
`cell-init.ps1` is the PowerShell 7 startup script for all cell profiles. It:
|
|
144
145
|
|
|
145
|
-
1. Accepts `
|
|
146
|
-
2. Looks up `
|
|
146
|
+
1. Accepts `Role` arg (`sv`, `cell-portal`, `cell-data`, `cell-cs2`, `cell-mktg`, `cell-infra`, `specialist`)
|
|
147
|
+
2. Looks up `Label`, `Color`, `Scope`, `Paths`, `Prompt` from the `$roles` hashtable
|
|
147
148
|
3. Prints a colored banner
|
|
148
149
|
4. Writes a PID lockfile to `.cell-state\<role>.lock`
|
|
149
150
|
5. Runs `git log` filtered to that cell's path scope
|
|
150
|
-
6.
|
|
151
|
-
7. Launches: `claude --append-system-prompt "<PROMPT_SCOPE>"`
|
|
151
|
+
6. Launches: `claude --append-system-prompt "<Prompt>"`
|
|
152
152
|
|
|
153
153
|
### Cell role → prompt scope mapping
|
|
154
154
|
|
|
@@ -184,9 +184,9 @@ Font defaults (in `profiles.defaults`):
|
|
|
184
184
|
|
|
185
185
|
### When editing cell startup
|
|
186
186
|
|
|
187
|
-
Edit `C:\Dev\regen-root\scripts\cell-init.
|
|
188
|
-
- Adding a new role: add a new
|
|
189
|
-
- Changing scope: update `
|
|
187
|
+
Edit `C:\Dev\regen-root\scripts\cell-init.ps1` — it's in the repo, so changes are tracked.
|
|
188
|
+
- Adding a new role: add a new entry to the `$roles` hashtable with all five keys (`Label`, `Color`, `Scope`, `Paths`, `Prompt`)
|
|
189
|
+
- Changing scope: update `Paths` and `Prompt` only — don't touch banner/lockfile/git logic
|
|
190
190
|
|
|
191
191
|
---
|
|
192
192
|
|
|
@@ -211,7 +211,7 @@ If Windows Terminal settings keep getting corrupted or misedited, **WezTerm** is
|
|
|
211
211
|
- Config is a Lua file (`~/.wezterm.lua` or `C:\Users\<user>\.config\wezterm\wezterm.lua`)
|
|
212
212
|
- Committed to git — every change is a reviewable diff, not an opaque JSON blob
|
|
213
213
|
- Startup layouts (tabs, panes, commands) are defined programmatically
|
|
214
|
-
- Equivalent to current setup: `wezterm.mux.spawn_window()` per cell with `args` set to `
|
|
214
|
+
- Equivalent to current setup: `wezterm.mux.spawn_window()` per cell with `args` set to `pwsh.exe -NoLogo -NoExit -File cell-init.ps1 <role>`
|
|
215
215
|
- Download: https://wezfurlong.org/wezterm/installation.html
|
|
216
216
|
|
|
217
217
|
Migration path: copy existing color schemes as `wezterm.color.get_default_colors()` override tables, map keybindings to `config.keys` array, define tab bar with cell roles.
|