autodev-cli 1.4.86 → 1.4.93

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.
@@ -0,0 +1,266 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.providerConsumesSkills = providerConsumesSkills;
37
+ exports.sanitizeRemoteSkills = sanitizeRemoteSkills;
38
+ exports.saveProjectSkills = saveProjectSkills;
39
+ exports.foldSkillsIntoProfile = foldSkillsIntoProfile;
40
+ const fs = __importStar(require("fs"));
41
+ const path = __importStar(require("path"));
42
+ const pathSafe_1 = require("./pathSafe");
43
+ // A skill slug is a plain directory name: lowercase alnum + hyphen only. No
44
+ // separators, no dots — this alone rejects "../evil", "a/b", ".hidden", etc.
45
+ const SKILL_NAME_RE = /^[a-z0-9][a-z0-9-]*$/;
46
+ // Caps — the whole point of the sanitizer. Keep them strict.
47
+ const MAX_SKILLS = 100; // total number of skills per sync
48
+ const MAX_NAME_LEN = 64; // slug length
49
+ const MAX_DESC_BYTES = 4 * 1024; // 4 KB per description
50
+ const MAX_BODY_BYTES = 64 * 1024; // 64 KB per body
51
+ const MAX_TOTAL_BYTES = 4 * 1024 * 1024; // 4 MB across all skills
52
+ const SKILLS_REL_DIR = path.posix.join('.claude', 'skills');
53
+ const MANIFEST_REL = path.join('.autodev', 'synced-skills.json');
54
+ /** True for providers that natively load `.claude/skills/<slug>/SKILL.md` (Claude only). */
55
+ function providerConsumesSkills(provider) {
56
+ return provider === 'claude-cli' || provider === 'claude-tui';
57
+ }
58
+ /**
59
+ * Validate an untrusted `skills` array (from a `skill_update` frame) down to the
60
+ * subset that is safe to write. Enforces slug shape, string types, per-file and
61
+ * aggregate size caps, a total count cap, and workspace path containment.
62
+ * Returns the accepted list (in order) plus the rejected entries with reasons.
63
+ * Callers must still gate the whole path behind an opt-in (skillUpdateEnabled).
64
+ */
65
+ function sanitizeRemoteSkills(root, skills) {
66
+ const safe = [];
67
+ const rejected = [];
68
+ if (!Array.isArray(skills)) {
69
+ return { safe, rejected };
70
+ }
71
+ const seen = new Set();
72
+ let totalBytes = 0;
73
+ for (const raw of skills) {
74
+ const entry = raw;
75
+ const rawName = typeof entry?.name === 'string' ? entry.name.trim() : '';
76
+ const label = rawName || '(unnamed)';
77
+ if (!entry || typeof entry !== 'object') {
78
+ rejected.push({ name: label, reason: 'not an object' });
79
+ continue;
80
+ }
81
+ if (!SKILL_NAME_RE.test(rawName)) {
82
+ rejected.push({ name: label, reason: 'invalid slug (must match ^[a-z0-9][a-z0-9-]*$)' });
83
+ continue;
84
+ }
85
+ if (rawName.length > MAX_NAME_LEN) {
86
+ rejected.push({ name: label, reason: 'slug too long' });
87
+ continue;
88
+ }
89
+ if (seen.has(rawName)) {
90
+ rejected.push({ name: rawName, reason: 'duplicate slug' });
91
+ continue;
92
+ }
93
+ if (typeof entry.description !== 'string') {
94
+ rejected.push({ name: rawName, reason: 'description not a string' });
95
+ continue;
96
+ }
97
+ if (typeof entry.body !== 'string') {
98
+ rejected.push({ name: rawName, reason: 'body not a string' });
99
+ continue;
100
+ }
101
+ const descBytes = Buffer.byteLength(entry.description, 'utf8');
102
+ const bodyBytes = Buffer.byteLength(entry.body, 'utf8');
103
+ if (descBytes > MAX_DESC_BYTES) {
104
+ rejected.push({ name: rawName, reason: `description too large (${descBytes} > ${MAX_DESC_BYTES})` });
105
+ continue;
106
+ }
107
+ if (bodyBytes > MAX_BODY_BYTES) {
108
+ rejected.push({ name: rawName, reason: `body too large (${bodyBytes} > ${MAX_BODY_BYTES})` });
109
+ continue;
110
+ }
111
+ // Path-safety: the composed target must resolve INSIDE the workspace, even
112
+ // after symlink resolution. (The slug regex already blocks ../ and /, this
113
+ // is belt-and-suspenders + symlink defence.)
114
+ const target = (0, pathSafe_1.resolveWithinRoot)(root, path.posix.join(SKILLS_REL_DIR, rawName, 'SKILL.md'), false);
115
+ if (!target) {
116
+ rejected.push({ name: rawName, reason: 'path escapes workspace' });
117
+ continue;
118
+ }
119
+ if (safe.length >= MAX_SKILLS) {
120
+ rejected.push({ name: rawName, reason: `over skill count cap (${MAX_SKILLS})` });
121
+ continue;
122
+ }
123
+ if (totalBytes + descBytes + bodyBytes > MAX_TOTAL_BYTES) {
124
+ rejected.push({ name: rawName, reason: `over total bytes cap (${MAX_TOTAL_BYTES})` });
125
+ continue;
126
+ }
127
+ totalBytes += descBytes + bodyBytes;
128
+ seen.add(rawName);
129
+ safe.push({ name: rawName, description: entry.description, body: entry.body });
130
+ }
131
+ return { safe, rejected };
132
+ }
133
+ /** Compose the on-disk SKILL.md (frontmatter + body). Description newlines are
134
+ * collapsed to keep the YAML frontmatter valid. */
135
+ function _composeSkillFile(s) {
136
+ const desc = s.description.replace(/[\r\n]+/g, ' ').trim();
137
+ const body = s.body.replace(/\s+$/, '');
138
+ return `---\nname: ${s.name}\ndescription: ${desc}\n---\n\n${body}\n`;
139
+ }
140
+ function _readManifest(root) {
141
+ try {
142
+ const f = path.join(root, MANIFEST_REL);
143
+ if (!fs.existsSync(f)) {
144
+ return [];
145
+ }
146
+ const arr = JSON.parse(fs.readFileSync(f, 'utf8'));
147
+ return Array.isArray(arr) ? arr.filter((x) => typeof x === 'string') : [];
148
+ }
149
+ catch {
150
+ return [];
151
+ }
152
+ }
153
+ function _writeManifest(root, slugs) {
154
+ const f = path.join(root, MANIFEST_REL);
155
+ const dir = path.dirname(f);
156
+ if (!fs.existsSync(dir)) {
157
+ fs.mkdirSync(dir, { recursive: true });
158
+ }
159
+ fs.writeFileSync(f, JSON.stringify(slugs, null, 2) + '\n', 'utf8');
160
+ }
161
+ /**
162
+ * Write the supplied (already-sanitized) skills to `<root>/.claude/skills/<slug>/SKILL.md`
163
+ * as a FULL REPLACE: every previously-synced skill (tracked in the manifest) that
164
+ * is NOT in the new set is removed, and its now-empty dir pruned. Skills we did
165
+ * not write (CLI-bundled via applyAllSkills, or user hand-made) are never touched.
166
+ * Returns the slugs written and removed.
167
+ */
168
+ function saveProjectSkills(root, safeSkills) {
169
+ const prev = _readManifest(root);
170
+ const nextSet = new Set(safeSkills.map(s => s.name));
171
+ const written = [];
172
+ const removed = [];
173
+ // Write / overwrite each safe skill.
174
+ for (const s of safeSkills) {
175
+ const target = (0, pathSafe_1.resolveWithinRoot)(root, path.posix.join(SKILLS_REL_DIR, s.name, 'SKILL.md'), false);
176
+ if (!target) {
177
+ continue;
178
+ } // defensive — sanitizer already validated
179
+ try {
180
+ const dir = path.dirname(target);
181
+ if (!fs.existsSync(dir)) {
182
+ fs.mkdirSync(dir, { recursive: true });
183
+ }
184
+ fs.writeFileSync(target, _composeSkillFile(s), 'utf8');
185
+ written.push(s.name);
186
+ }
187
+ catch { /* ignore individual write errors */ }
188
+ }
189
+ // Remove previously-synced skills that are gone from the new set — but ONLY
190
+ // ones we own (in the manifest), never bundled or hand-made skills.
191
+ for (const slug of prev) {
192
+ if (nextSet.has(slug)) {
193
+ continue;
194
+ }
195
+ if (!SKILL_NAME_RE.test(slug)) {
196
+ continue;
197
+ } // defensive
198
+ const target = (0, pathSafe_1.resolveWithinRoot)(root, path.posix.join(SKILLS_REL_DIR, slug, 'SKILL.md'), false);
199
+ if (!target) {
200
+ continue;
201
+ }
202
+ try {
203
+ if (fs.existsSync(target)) {
204
+ fs.unlinkSync(target);
205
+ }
206
+ const dir = path.dirname(target);
207
+ if (fs.existsSync(dir) && fs.readdirSync(dir).length === 0) {
208
+ fs.rmdirSync(dir);
209
+ }
210
+ removed.push(slug);
211
+ }
212
+ catch { /* ignore */ }
213
+ }
214
+ _writeManifest(root, written);
215
+ return { written, removed };
216
+ }
217
+ // ---------------------------------------------------------------------------
218
+ // Prose fold — for non-Claude providers (opencode/grok/copilot) that cannot
219
+ // load SKILL.md, fold a short managed "## Skills" section into AGENTS.md so
220
+ // those agents still benefit. Idempotent: replaces a marker block, never
221
+ // appends forever. Passing an empty list removes the block.
222
+ // ---------------------------------------------------------------------------
223
+ const SKILLS_FOLD_BEGIN = '<!-- autodev:skills:begin -->';
224
+ const SKILLS_FOLD_END = '<!-- autodev:skills:end -->';
225
+ const SKILLS_FOLD_RE = /\n*<!-- autodev:skills:begin -->[\s\S]*?<!-- autodev:skills:end -->\n*/;
226
+ /**
227
+ * Fold the given skills into a managed "## Skills" block in `<root>/AGENTS.md`
228
+ * (best-effort, idempotent). Non-Claude agents read AGENTS.md but cannot load
229
+ * `.claude/skills`, so this is how they gain the skill instructions. An empty
230
+ * list strips the block.
231
+ */
232
+ function foldSkillsIntoProfile(root, safeSkills) {
233
+ if (!root) {
234
+ return;
235
+ }
236
+ const target = path.join(root, 'AGENTS.md');
237
+ let block = '';
238
+ if (safeSkills.length > 0) {
239
+ const parts = safeSkills.map(s => {
240
+ const desc = s.description.replace(/[\r\n]+/g, ' ').trim();
241
+ return `### ${s.name}\n\n${desc}\n\n${s.body.trim()}`;
242
+ });
243
+ block = `${SKILLS_FOLD_BEGIN}\n\n## Skills\n\n${parts.join('\n\n')}\n\n${SKILLS_FOLD_END}`;
244
+ }
245
+ let content = '';
246
+ try {
247
+ if (fs.existsSync(target)) {
248
+ content = fs.readFileSync(target, 'utf8');
249
+ }
250
+ }
251
+ catch { /* treat as empty */ }
252
+ if (SKILLS_FOLD_RE.test(content)) {
253
+ content = content.replace(SKILLS_FOLD_RE, block ? `\n\n${block}\n` : '\n');
254
+ }
255
+ else if (block) {
256
+ content = content ? `${content.replace(/\s+$/, '')}\n\n${block}\n` : `${block}\n`;
257
+ }
258
+ else {
259
+ return; // nothing to add, nothing to strip
260
+ }
261
+ try {
262
+ fs.writeFileSync(target, content, 'utf8');
263
+ }
264
+ catch { /* best-effort */ }
265
+ }
266
+ //# sourceMappingURL=projectSkills.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"projectSkills.js","sourceRoot":"","sources":["../../src/core/projectSkills.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Redact known secret shapes from a single string. Applies every pattern in
3
+ * order (specific before generic). Returns the string unchanged when it holds
4
+ * no secrets. Safe on empty / non-secret prose.
5
+ */
6
+ export declare function redactSecrets(text: string): string;
7
+ /**
8
+ * Deep-redact every STRING value inside an object/array, leaving structure and
9
+ * KEYS intact. Hook events forwarded to the office are objects (not flat
10
+ * strings) — their tool_input.command, tool_response, message text, etc. can
11
+ * each carry a secret — so we walk the whole value and run `redactSecrets` on
12
+ * every string leaf. Non-string leaves (numbers, booleans, null) pass through
13
+ * untouched. Returns a NEW value; the input is not mutated.
14
+ */
15
+ export declare function redactDeep<T>(value: T): T;
@@ -0,0 +1,110 @@
1
+ "use strict";
2
+ // Secrets anonymizer for OUTBOUND agent content.
3
+ //
4
+ // Agent-originated content (tool calls/output, bash output, file reads,
5
+ // assistant prose) is forwarded from the CLI to the pixel-office, where it is
6
+ // STORED and DISPLAYED in the office feed/chat. Secrets (API keys, tokens) can
7
+ // leak into that stream. We redact them HERE, before the content leaves the
8
+ // machine — mirroring how the tutor project redacts its trace files.
9
+ //
10
+ // Scope: this is applied ONLY to outbound content going to the office (hook
11
+ // events, assistant narration, autonomy task results / A2A replies). It is NOT
12
+ // applied to the model's actual input, local files on disk, or the api_key used
13
+ // for auth (that travels as an HTTP header, not as content).
14
+ //
15
+ // Performance: the pattern list is compiled ONCE at module load (the const
16
+ // array below) and reused for every call. `redactSecrets` runs on every
17
+ // forwarded event, so we never rebuild RegExp objects per-call.
18
+ //
19
+ // Ordering: patterns run in array order — SPECIFIC shapes before GENERIC ones
20
+ // (e.g. `sk-ant-` before the generic `sk-` key, so an Anthropic key is labelled
21
+ // as such and its remainder can't be re-matched by a broader rule).
22
+ //
23
+ // The min-length bounds in each pattern are deliberate: they stop the generic
24
+ // rules from mangling ordinary prose. Do NOT loosen them.
25
+ Object.defineProperty(exports, "__esModule", { value: true });
26
+ exports.redactSecrets = redactSecrets;
27
+ exports.redactDeep = redactDeep;
28
+ // NOTE on emails: the reference trace-redactor also maps plain email addresses
29
+ // (PII). We DELIBERATELY do NOT redact emails here — user emails legitimately
30
+ // appear in the office UI (character owners, teammates), and redacting them
31
+ // would mangle normal content. So no email pattern is included.
32
+ const PATTERNS = [
33
+ // Anthropic API keys — must run BEFORE the generic `sk-` rule.
34
+ { name: 'anthropic_key', re: /sk-ant-[A-Za-z0-9_-]{15,}/g, replacement: 'sk-ant-REDACTED' },
35
+ // Generic OpenAI-style `sk-…` keys.
36
+ { name: 'openai_style_key', re: /sk-[A-Za-z0-9]{20,}/g, replacement: 'sk-REDACTED' },
37
+ // GitHub tokens: gho_/ghp_/ghr_/ghs_/ghu_.
38
+ { name: 'gh_token', re: /gh[oprsu]_[A-Za-z0-9]{20,}/g, replacement: 'gh_REDACTED' },
39
+ // GitHub fine-grained PATs.
40
+ { name: 'gh_pat', re: /github_pat_[A-Za-z0-9_]{20,}/g, replacement: 'github_pat_REDACTED' },
41
+ // Google API keys.
42
+ { name: 'google_api_key', re: /AIza[0-9A-Za-z_-]{25,}/g, replacement: 'AIza_REDACTED' },
43
+ // Slack tokens: xoxb/xoxa/xoxp/xoxr/xoxs.
44
+ { name: 'slack_token', re: /xox[baprs]-[0-9A-Za-z-]{20,}/g, replacement: 'xoxX-REDACTED' },
45
+ // AWS access key IDs.
46
+ { name: 'aws_access_key', re: /AKIA[0-9A-Z]{16}/g, replacement: 'AKIA_REDACTED' },
47
+ // Stripe secret/publishable/restricted live+test keys.
48
+ { name: 'stripe_key', re: /(?:sk|pk|rk)_(?:live|test)_[0-9A-Za-z]{20,}/g, replacement: 'stripe_REDACTED' },
49
+ // JWTs (three base64url segments).
50
+ { name: 'jwt_token', re: /eyJ[A-Za-z0-9_-]{10,}\.eyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}/g, replacement: 'eyJ.REDACTED.JWT' },
51
+ // `Bearer <token>` — keep the "Bearer " prefix, redact the credential.
52
+ { name: 'bearer_token', re: /([Bb]earer\s+)[A-Za-z0-9._~+/=-]{20,}/g, replacement: '$1REDACTED_BEARER' },
53
+ // PEM private-key blocks (RSA / EC / OPENSSH / generic).
54
+ { name: 'private_key', re: /-----BEGIN [A-Z0-9 ]*PRIVATE KEY-----[\s\S]*?-----END [A-Z0-9 ]*PRIVATE KEY-----/g, replacement: '-----REDACTED_PRIVATE_KEY-----' },
55
+ // JSON `"password": "…"` values.
56
+ { name: 'password_json', re: /("[Pp]ass(?:word|wd)"\s*:\s*")[^"\\]+(")/g, replacement: '$1REDACTED$2' },
57
+ // JSON `"token"/"auth_token"/"api_token"` values.
58
+ { name: 'token_json', re: /("(?:[Aa]uth|[Aa]pi)?_?[Tt]oken"\s*:\s*")[^"\\]{15,}(")/g, replacement: '$1REDACTED$2' },
59
+ // Shell/env `FOO_TOKEN=…`, `API_KEY: …`, `*_SECRET=…`, `*_PASSWORD=…`.
60
+ { name: 'env_var_secret', re: /([A-Z][A-Z0-9_]*(?:TOKEN|SECRET|API[_-]?KEY|PASSWORD|PASSWD)[A-Z0-9_]*\s*[:=]\s*["']?)[A-Za-z0-9/_+.=-]{15,}/g, replacement: '$1REDACTED' },
61
+ // ── Our own pixel-office agent credential shapes ────────────────────────────
62
+ // These are the office agent api_keys (`agt_…`) and personal access tokens
63
+ // (`pat_…`). They authenticate a character to the office and must NEVER show
64
+ // up in the office feed itself.
65
+ { name: 'agt_key', re: /agt_[A-Za-z0-9]{16,}/g, replacement: 'agt_REDACTED' },
66
+ { name: 'pat_key', re: /pat_[A-Za-z0-9]{16,}/g, replacement: 'pat_REDACTED' },
67
+ ];
68
+ /**
69
+ * Redact known secret shapes from a single string. Applies every pattern in
70
+ * order (specific before generic). Returns the string unchanged when it holds
71
+ * no secrets. Safe on empty / non-secret prose.
72
+ */
73
+ function redactSecrets(text) {
74
+ if (!text) {
75
+ return text;
76
+ }
77
+ let out = text;
78
+ for (const p of PATTERNS) {
79
+ // `re` is a shared module-level RegExp with the global flag. String.replace
80
+ // does not rely on / advance its lastIndex, so reuse is safe and allocation-
81
+ // free (unlike RegExp.exec, which would need a lastIndex reset).
82
+ out = out.replace(p.re, p.replacement);
83
+ }
84
+ return out;
85
+ }
86
+ /**
87
+ * Deep-redact every STRING value inside an object/array, leaving structure and
88
+ * KEYS intact. Hook events forwarded to the office are objects (not flat
89
+ * strings) — their tool_input.command, tool_response, message text, etc. can
90
+ * each carry a secret — so we walk the whole value and run `redactSecrets` on
91
+ * every string leaf. Non-string leaves (numbers, booleans, null) pass through
92
+ * untouched. Returns a NEW value; the input is not mutated.
93
+ */
94
+ function redactDeep(value) {
95
+ if (typeof value === 'string') {
96
+ return redactSecrets(value);
97
+ }
98
+ if (Array.isArray(value)) {
99
+ return value.map((v) => redactDeep(v));
100
+ }
101
+ if (value && typeof value === 'object') {
102
+ const out = {};
103
+ for (const [k, v] of Object.entries(value)) {
104
+ out[k] = redactDeep(v);
105
+ }
106
+ return out;
107
+ }
108
+ return value;
109
+ }
110
+ //# sourceMappingURL=redactSecrets.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"redactSecrets.js","sourceRoot":"","sources":["../../src/core/redactSecrets.ts"],"names":[],"mappings":";AAAA,iDAAiD;AACjD,EAAE;AACF,wEAAwE;AACxE,8EAA8E;AAC9E,+EAA+E;AAC/E,4EAA4E;AAC5E,qEAAqE;AACrE,EAAE;AACF,4EAA4E;AAC5E,+EAA+E;AAC/E,gFAAgF;AAChF,6DAA6D;AAC7D,EAAE;AACF,2EAA2E;AAC3E,wEAAwE;AACxE,gEAAgE;AAChE,EAAE;AACF,8EAA8E;AAC9E,gFAAgF;AAChF,oEAAoE;AACpE,EAAE;AACF,8EAA8E;AAC9E,0DAA0D;;AAuD1D,sCAUC;AAUD,gCAeC;AAlFD,+EAA+E;AAC/E,8EAA8E;AAC9E,4EAA4E;AAC5E,gEAAgE;AAEhE,MAAM,QAAQ,GAA6B;IACzC,+DAA+D;IAC/D,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,4BAA4B,EAAE,WAAW,EAAE,iBAAiB,EAAE;IAC3F,oCAAoC;IACpC,EAAE,IAAI,EAAE,kBAAkB,EAAE,EAAE,EAAE,sBAAsB,EAAE,WAAW,EAAE,aAAa,EAAE;IACpF,2CAA2C;IAC3C,EAAE,IAAI,EAAE,UAAU,EAAE,EAAE,EAAE,6BAA6B,EAAE,WAAW,EAAE,aAAa,EAAE;IACnF,4BAA4B;IAC5B,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,+BAA+B,EAAE,WAAW,EAAE,qBAAqB,EAAE;IAC3F,mBAAmB;IACnB,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,yBAAyB,EAAE,WAAW,EAAE,eAAe,EAAE;IACvF,0CAA0C;IAC1C,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,+BAA+B,EAAE,WAAW,EAAE,eAAe,EAAE;IAC1F,sBAAsB;IACtB,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,mBAAmB,EAAE,WAAW,EAAE,eAAe,EAAE;IACjF,uDAAuD;IACvD,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,8CAA8C,EAAE,WAAW,EAAE,iBAAiB,EAAE;IAC1G,mCAAmC;IACnC,EAAE,IAAI,EAAE,WAAW,EAAE,EAAE,EAAE,mEAAmE,EAAE,WAAW,EAAE,kBAAkB,EAAE;IAC/H,uEAAuE;IACvE,EAAE,IAAI,EAAE,cAAc,EAAE,EAAE,EAAE,wCAAwC,EAAE,WAAW,EAAE,mBAAmB,EAAE;IACxG,yDAAyD;IACzD,EAAE,IAAI,EAAE,aAAa,EAAE,EAAE,EAAE,mFAAmF,EAAE,WAAW,EAAE,gCAAgC,EAAE;IAC/J,iCAAiC;IACjC,EAAE,IAAI,EAAE,eAAe,EAAE,EAAE,EAAE,2CAA2C,EAAE,WAAW,EAAE,cAAc,EAAE;IACvG,kDAAkD;IAClD,EAAE,IAAI,EAAE,YAAY,EAAE,EAAE,EAAE,0DAA0D,EAAE,WAAW,EAAE,cAAc,EAAE;IACnH,uEAAuE;IACvE,EAAE,IAAI,EAAE,gBAAgB,EAAE,EAAE,EAAE,+GAA+G,EAAE,WAAW,EAAE,YAAY,EAAE;IAC1K,+EAA+E;IAC/E,2EAA2E;IAC3E,6EAA6E;IAC7E,gCAAgC;IAChC,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,uBAAuB,EAAE,WAAW,EAAE,cAAc,EAAE;IAC7E,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,EAAE,uBAAuB,EAAE,WAAW,EAAE,cAAc,EAAE;CAC9E,CAAC;AAEF;;;;GAIG;AACH,SAAgB,aAAa,CAAC,IAAY;IACxC,IAAI,CAAC,IAAI,EAAE,CAAC;QAAC,OAAO,IAAI,CAAC;IAAC,CAAC;IAC3B,IAAI,GAAG,GAAG,IAAI,CAAC;IACf,KAAK,MAAM,CAAC,IAAI,QAAQ,EAAE,CAAC;QACzB,4EAA4E;QAC5E,6EAA6E;QAC7E,iEAAiE;QACjE,GAAG,GAAG,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,CAAC;IACzC,CAAC;IACD,OAAO,GAAG,CAAC;AACb,CAAC;AAED;;;;;;;GAOG;AACH,SAAgB,UAAU,CAAI,KAAQ;IACpC,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QAC9B,OAAO,aAAa,CAAC,KAAK,CAAiB,CAAC;IAC9C,CAAC;IACD,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QACzB,OAAO,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC,CAAiB,CAAC;IACzD,CAAC;IACD,IAAI,KAAK,IAAI,OAAO,KAAK,KAAK,QAAQ,EAAE,CAAC;QACvC,MAAM,GAAG,GAA4B,EAAE,CAAC;QACxC,KAAK,MAAM,CAAC,CAAC,EAAE,CAAC,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,KAAgC,CAAC,EAAE,CAAC;YACtE,GAAG,CAAC,CAAC,CAAC,GAAG,UAAU,CAAC,CAAC,CAAC,CAAC;QACzB,CAAC;QACD,OAAO,GAAmB,CAAC;IAC7B,CAAC;IACD,OAAO,KAAK,CAAC;AACf,CAAC"}
@@ -40,6 +40,15 @@ export interface AutodevSettings {
40
40
  * enableFileBrowser / gitEnabled). Even when enabled, entries are validated.
41
41
  */
42
42
  mcpUpdateEnabled: boolean;
43
+ /**
44
+ * Opt-in: honor `skill_update` frames pushed over the WS channel. When false
45
+ * (default), inbound skill_update frames are ignored. Writing remote-supplied
46
+ * skills drops instruction files into `.claude/skills/` that a running Claude
47
+ * agent live-reads, so it is an instruction-injection surface and must be
48
+ * explicitly enabled (mirrors mcpUpdateEnabled). Even when enabled, each skill
49
+ * is validated (slug shape, size caps, workspace path containment).
50
+ */
51
+ skillUpdateEnabled: boolean;
43
52
  hooksEnabled: boolean;
44
53
  hooksScope: 'project' | 'global';
45
54
  openCodeHooksEnabled: boolean;
@@ -71,6 +71,7 @@ exports.SETTINGS_DEFAULTS = {
71
71
  enableFileBrowser: false,
72
72
  gitEnabled: false,
73
73
  mcpUpdateEnabled: false,
74
+ skillUpdateEnabled: false,
74
75
  hooksEnabled: false,
75
76
  hooksScope: 'project',
76
77
  openCodeHooksEnabled: false,
@@ -1 +1 @@
1
- {"version":3,"file":"settingsLoader.js","sourceRoot":"","sources":["../../src/core/settingsLoader.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"settingsLoader.js","sourceRoot":"","sources":["../../src/core/settingsLoader.ts"],"names":[],"mappings":"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"}
package/out/taskLoop.d.ts CHANGED
@@ -108,6 +108,14 @@ export declare class TaskLoopRunner {
108
108
  restart(): Promise<void>;
109
109
  /** Handle mcp_update pushed from pixel-office: write .mcp.json, sync all providers, restart loop. */
110
110
  private _handleMcpUpdate;
111
+ /**
112
+ * Handle skill_update pushed from pixel-office: validate + write the agent's
113
+ * FULL effective skill set to `.claude/skills/<slug>/SKILL.md`. Skills
114
+ * live-reload (Claude re-reads them each run), so this does NOT restart the
115
+ * loop — it just sanitizes, full-replaces on disk, folds a prose block into
116
+ * AGENTS.md for non-Claude providers, and reports the applied names.
117
+ */
118
+ private _handleSkillUpdate;
111
119
  /** Handle export_request from pixel-office: create backup zip and upload. */
112
120
  private _handleExportRequest;
113
121
  /** Handle restore_request from pixel-office: download zip and restore workspace. */
package/out/taskLoop.js CHANGED
@@ -61,7 +61,9 @@ const periodicActions_1 = require("./periodicActions");
61
61
  const discordGateway_1 = require("./discordGateway");
62
62
  const webhookPoller_1 = require("./webhookPoller");
63
63
  const emailPoller_1 = require("./emailPoller");
64
+ const modelInfo_1 = require("./core/modelInfo");
64
65
  const projectMcp_1 = require("./core/projectMcp");
66
+ const projectSkills_1 = require("./core/projectSkills");
65
67
  const configManager_1 = require("./configManager");
66
68
  const agentBackup_1 = require("./agentBackup");
67
69
  const version_1 = require("./version");
@@ -71,6 +73,7 @@ const version_1 = require("./version");
71
73
  const rateLimit_1 = require("./rateLimit");
72
74
  const cliExit_1 = require("./cliExit");
73
75
  const liveNarration_1 = require("./core/liveNarration");
76
+ const redactSecrets_1 = require("./core/redactSecrets");
74
77
  class ContextLengthError extends Error {
75
78
  rawMessage;
76
79
  constructor(rawMessage) {
@@ -393,7 +396,13 @@ class TaskLoopRunner {
393
396
  // `cliVersion` rides in the meta merged into every webhook frame — notably
394
397
  // the agent_online (hello) frame — so pixel-office can record which CLI
395
398
  // version each agent runs (surfacing stale, steer-incapable agents).
396
- this._webhook?.setMeta({ provider: settings.provider, cliVersion: version_1.CLI_VERSION, workDir: root, hostname: this._hostname, gitRepo: this._gitRepo, gitBranch: this._gitBranch });
399
+ // `model` the effective friendly model name (Opus 4.8 / kimi-k2.7-code /
400
+ // grok-code). Resolved from the provider's configured override; undefined
401
+ // when the provider runs its account default (office gates the field). Rides
402
+ // in setMeta so it merges into every frame, notably agent_online, where the
403
+ // office records it against the agent for the per-agent model badge.
404
+ const effectiveModel = (0, modelInfo_1.resolveConfiguredModel)(settings);
405
+ this._webhook?.setMeta({ provider: settings.provider, cliVersion: version_1.CLI_VERSION, model: effectiveModel, workDir: root, hostname: this._hostname, gitRepo: this._gitRepo, gitBranch: this._gitBranch });
397
406
  this._discordPoller = (settings.discordToken && settings.discordChannelId && settings.discordOwners)
398
407
  ? new discordPoller_1.DiscordPoller(settings.discordToken, settings.discordChannelId, settings.discordOwners)
399
408
  : null;
@@ -440,6 +449,7 @@ class TaskLoopRunner {
440
449
  // mis-detected as 'claude' by the office's hook-based fallback.
441
450
  provider: this._settings?.provider,
442
451
  cliVersion: version_1.CLI_VERSION,
452
+ model: this._settings ? (0, modelInfo_1.resolveConfiguredModel)(this._settings) : undefined,
443
453
  hostname: this._hostname,
444
454
  workDir: this._workspaceRoot ?? '',
445
455
  gitRepo: this._gitRepo,
@@ -491,6 +501,7 @@ class TaskLoopRunner {
491
501
  this._webhookPoller.setOnSteer((text, onDelivered) => void this._handleSteer(text, onDelivered));
492
502
  this._webhookPoller.setOnCommand((cmd) => this._handleCommand(cmd));
493
503
  this._webhookPoller.setOnMcpUpdate((entries) => this._handleMcpUpdate(entries));
504
+ this._webhookPoller.setOnSkillUpdate((skills) => this._handleSkillUpdate(skills));
494
505
  this._webhookPoller.setOnExportRequest((agentId) => void this._handleExportRequest(agentId));
495
506
  this._webhookPoller.setOnRestoreRequest((agentId, downloadUrl) => void this._handleRestoreRequest(agentId, downloadUrl));
496
507
  this._webhookPoller.setOnExportConfig((exportEnabled, exportDailyBackup, agentId) => this._handleExportConfig(exportEnabled, exportDailyBackup, agentId));
@@ -562,6 +573,7 @@ class TaskLoopRunner {
562
573
  this._notifyWebhook('agent_online', {
563
574
  provider: settings.provider,
564
575
  cliVersion: version_1.CLI_VERSION,
576
+ model: effectiveModel,
565
577
  hostname: this._hostname,
566
578
  workDir: root,
567
579
  gitRepo: this._gitRepo,
@@ -710,6 +722,44 @@ class TaskLoopRunner {
710
722
  }
711
723
  void this.restart();
712
724
  }
725
+ /**
726
+ * Handle skill_update pushed from pixel-office: validate + write the agent's
727
+ * FULL effective skill set to `.claude/skills/<slug>/SKILL.md`. Skills
728
+ * live-reload (Claude re-reads them each run), so this does NOT restart the
729
+ * loop — it just sanitizes, full-replaces on disk, folds a prose block into
730
+ * AGENTS.md for non-Claude providers, and reports the applied names.
731
+ */
732
+ _handleSkillUpdate(skills) {
733
+ const root = this._workspaceRoot;
734
+ if (!root)
735
+ return;
736
+ // Opt-in gate: remote-supplied skills become instruction files a running
737
+ // Claude agent live-reads. Ignore unless explicitly enabled, mirroring
738
+ // mcpUpdateEnabled / enableFileBrowser / gitEnabled.
739
+ if (!this._settings?.skillUpdateEnabled) {
740
+ this._cb?.log('🔒 skill_update ignored — skillUpdateEnabled is off (set it in .autodev/settings.json to allow)');
741
+ return;
742
+ }
743
+ this._cb?.log('🧩 skill_update received — validating and writing .claude/skills…');
744
+ const { safe, rejected } = (0, projectSkills_1.sanitizeRemoteSkills)(root, skills);
745
+ if (rejected.length) {
746
+ this._cb?.log(`⚠️ skill_update dropped ${rejected.length} entr${rejected.length === 1 ? 'y' : 'ies'}: ${rejected.map(r => `${r.name} (${r.reason})`).join(', ')}`);
747
+ }
748
+ try {
749
+ // Full-replace: writes the safe set and prunes previously-synced skills
750
+ // that are gone (even when `safe` is empty — that clears them all).
751
+ const { written, removed } = (0, projectSkills_1.saveProjectSkills)(root, safe);
752
+ // Non-Claude providers can't load SKILL.md — fold a prose block into AGENTS.md.
753
+ if (!(0, projectSkills_1.providerConsumesSkills)(this._settings?.provider)) {
754
+ (0, projectSkills_1.foldSkillsIntoProfile)(root, safe);
755
+ }
756
+ void configManager_1.ConfigManager.reportProjectSkills(root, written, (m) => this._cb?.log(m));
757
+ this._cb?.log(`✅ skills synced — wrote ${written.length}, removed ${removed.length} (.claude/skills). Live-reloads on the next run — no restart.`);
758
+ }
759
+ catch (err) {
760
+ this._cb?.log(`⚠️ skill update failed: ${err instanceof Error ? err.message : String(err)}`);
761
+ }
762
+ }
713
763
  /** Handle export_request from pixel-office: create backup zip and upload. */
714
764
  async _handleExportRequest(agentId) {
715
765
  const root = this._workspaceRoot;
@@ -1168,7 +1218,10 @@ class TaskLoopRunner {
1168
1218
  if (sessionName) {
1169
1219
  ev._session_name = sessionName;
1170
1220
  }
1171
- this._webhookPoller.sendFrame({ type: 'hook_event', data: ev });
1221
+ // Redact secrets from the whole hook object (tool_input/tool_response/
1222
+ // assistant narration/file contents) before it leaves the machine —
1223
+ // this stream is stored + rendered in the office feed/chat.
1224
+ this._webhookPoller.sendFrame({ type: 'hook_event', data: (0, redactSecrets_1.redactDeep)(ev) });
1172
1225
  }
1173
1226
  catch { /* skip malformed lines */ }
1174
1227
  }