@bastani/atomic 0.9.0-alpha.3 → 0.9.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/CHANGELOG.md +48 -0
- package/dist/builtin/cursor/CHANGELOG.md +7 -0
- package/dist/builtin/cursor/package.json +2 -2
- package/dist/builtin/intercom/CHANGELOG.md +8 -0
- package/dist/builtin/intercom/package.json +1 -1
- package/dist/builtin/mcp/CHANGELOG.md +12 -0
- package/dist/builtin/mcp/package.json +1 -1
- package/dist/builtin/subagents/CHANGELOG.md +17 -0
- package/dist/builtin/subagents/package.json +1 -1
- package/dist/builtin/web-access/CHANGELOG.md +8 -0
- package/dist/builtin/web-access/package.json +1 -1
- package/dist/builtin/workflows/CHANGELOG.md +50 -0
- package/dist/builtin/workflows/README.md +12 -12
- package/dist/builtin/workflows/builtin/goal-prompts.ts +8 -0
- package/dist/builtin/workflows/builtin/goal-runner.ts +96 -1
- package/dist/builtin/workflows/builtin/goal-types.ts +2 -0
- package/dist/builtin/workflows/builtin/goal.d.ts +3 -0
- package/dist/builtin/workflows/builtin/goal.ts +12 -1
- package/dist/builtin/workflows/builtin/index.d.ts +8 -8
- package/dist/builtin/workflows/builtin/open-claude-design-feedback.ts +359 -0
- package/dist/builtin/workflows/builtin/open-claude-design-phases.ts +254 -352
- package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +256 -414
- package/dist/builtin/workflows/builtin/open-claude-design-setup.ts +272 -0
- package/dist/builtin/workflows/builtin/open-claude-design-utils.ts +58 -68
- package/dist/builtin/workflows/builtin/open-claude-design.d.ts +5 -9
- package/dist/builtin/workflows/builtin/open-claude-design.ts +14 -26
- package/dist/builtin/workflows/package.json +1 -1
- package/dist/builtin/workflows/skills/impeccable/SKILL.md +14 -23
- package/dist/builtin/workflows/skills/impeccable/reference/brand.md +2 -2
- package/dist/builtin/workflows/skills/impeccable/reference/live.md +25 -4
- package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +724 -29
- package/dist/builtin/workflows/skills/impeccable/scripts/critique-storage.mjs +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +219 -7
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +57 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +750 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +648 -53
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns.mjs +7 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/browser/detect-url.mjs +29 -4
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +44 -11
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/css-cascade.mjs +29 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/static-html/detect-html.mjs +27 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/node/file-system.mjs +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +29 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +401 -46
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/inline-ignores.mjs +148 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +6 -6
- package/dist/builtin/workflows/skills/impeccable/scripts/{design-parser.mjs → lib/design-parser.mjs} +8 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +638 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-paths.mjs +128 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/{is-generated.mjs → lib/is-generated.mjs} +2 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/lib/target-args.mjs +42 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/browser-script-parts.mjs +49 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/{live-completion.mjs → live/completion.mjs} +1 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/{live-event-validation.mjs → live/event-validation.mjs} +6 -5
- package/dist/builtin/workflows/skills/impeccable/scripts/live/manual-apply.mjs +939 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/manual-edit-routes.mjs +357 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/{live-manual-edits-buffer.mjs → live/manual-edits-buffer.mjs} +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/{live-session-store.mjs → live/session-store.mjs} +21 -3
- package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +835 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/sveltekit-adapter.mjs +274 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/ui-core.mjs +180 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live/vocabulary.mjs +36 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +185 -60
- package/dist/builtin/workflows/skills/impeccable/scripts/live-browser-dom.js +146 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +3369 -1026
- package/dist/builtin/workflows/skills/impeccable/scripts/live-commit-manual-edits.mjs +2 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/live-complete.mjs +2 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/live-discard-manual-edits.mjs +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/live-inject.mjs +133 -9
- package/dist/builtin/workflows/skills/impeccable/scripts/live-insert.mjs +42 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/live-manual-edit-evidence.mjs +4 -4
- package/dist/builtin/workflows/skills/impeccable/scripts/live-poll.mjs +21 -15
- package/dist/builtin/workflows/skills/impeccable/scripts/live-resume.mjs +1 -1
- package/dist/builtin/workflows/skills/impeccable/scripts/live-server.mjs +205 -1269
- package/dist/builtin/workflows/skills/impeccable/scripts/live-status.mjs +2 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/live-target.mjs +30 -0
- package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +69 -26
- package/dist/builtin/workflows/skills/impeccable/scripts/live.mjs +73 -22
- package/dist/core/atomic-guide-command.d.ts.map +1 -1
- package/dist/core/atomic-guide-command.js +5 -5
- package/dist/core/atomic-guide-command.js.map +1 -1
- package/docs/index.md +2 -2
- package/docs/quickstart.md +9 -9
- package/docs/workflows.md +42 -23
- package/package.json +2 -2
- package/dist/builtin/workflows/skills/impeccable/scripts/cleanup-deprecated.mjs +0 -284
- package/dist/builtin/workflows/skills/impeccable/scripts/impeccable-paths.mjs +0 -126
- /package/dist/builtin/workflows/skills/impeccable/scripts/{live-insert-ui.mjs → live/insert-ui.mjs} +0 -0
|
@@ -16,8 +16,8 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { buildManualEditEvidence } from './live-manual-edit-evidence.mjs';
|
|
19
|
-
import { readBuffer, readBufferStrict, writeBuffer, countByPage } from './live
|
|
20
|
-
import { isGeneratedFile } from './is-generated.mjs';
|
|
19
|
+
import { readBuffer, readBufferStrict, writeBuffer, countByPage } from './live/manual-edits-buffer.mjs';
|
|
20
|
+
import { isGeneratedFile } from './lib/is-generated.mjs';
|
|
21
21
|
import {
|
|
22
22
|
runCopyEditBatchAgent,
|
|
23
23
|
runCopyEditPostApplyChecks,
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
* Canonical durable completion acknowledgement for Impeccable live sessions.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createLiveSessionStore } from './live
|
|
7
|
-
import { readLiveServerInfo } from './impeccable-paths.mjs';
|
|
6
|
+
import { createLiveSessionStore } from './live/session-store.mjs';
|
|
7
|
+
import { readLiveServerInfo } from './lib/impeccable-paths.mjs';
|
|
8
8
|
|
|
9
9
|
function parseArgs(argv) {
|
|
10
10
|
const out = { status: 'complete' };
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* Output JSON: { discarded: N, entries: [...discardedEntries], totalCount: N }
|
|
17
17
|
*/
|
|
18
18
|
|
|
19
|
-
import { readBuffer, removeEntries, truncateBuffer } from './live
|
|
19
|
+
import { readBuffer, removeEntries, truncateBuffer } from './live/manual-edits-buffer.mjs';
|
|
20
20
|
|
|
21
21
|
function argVal(args, name) {
|
|
22
22
|
const prefix = name + '=';
|
|
@@ -16,12 +16,41 @@
|
|
|
16
16
|
import fs from 'node:fs';
|
|
17
17
|
import path from 'node:path';
|
|
18
18
|
import { fileURLToPath } from 'node:url';
|
|
19
|
-
import { resolveLiveConfigPath } from './impeccable-paths.mjs';
|
|
19
|
+
import { resolveLiveConfigPath } from './lib/impeccable-paths.mjs';
|
|
20
|
+
import {
|
|
21
|
+
applySvelteKitLiveAdapter,
|
|
22
|
+
detectSvelteKitProject,
|
|
23
|
+
removeSvelteKitLiveAdapter,
|
|
24
|
+
} from './live/sveltekit-adapter.mjs';
|
|
20
25
|
|
|
21
26
|
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
22
27
|
const CONFIG_PATH = resolveLiveConfigPath({ cwd: process.cwd(), scriptsDir: __dirname });
|
|
23
28
|
const MARKER_OPEN_TEXT = 'impeccable-live-start';
|
|
24
29
|
const MARKER_CLOSE_TEXT = 'impeccable-live-end';
|
|
30
|
+
const IGNORE_MARKER_OPEN = '# impeccable-live-ignore-start';
|
|
31
|
+
const IGNORE_MARKER_CLOSE = '# impeccable-live-ignore-end';
|
|
32
|
+
|
|
33
|
+
export const LIVE_IGNORE_PATTERNS = Object.freeze([
|
|
34
|
+
'.impeccable/hook.cache.json',
|
|
35
|
+
'.impeccable/hook.pending.json',
|
|
36
|
+
'.impeccable/config.local.json',
|
|
37
|
+
'.impeccable/live/server.json',
|
|
38
|
+
'.impeccable/live/sessions/',
|
|
39
|
+
'.impeccable/live/previews/',
|
|
40
|
+
'.impeccable/live/annotations/',
|
|
41
|
+
'.impeccable/live/cache/',
|
|
42
|
+
'.impeccable/live/manual-edit-apply-transaction.json',
|
|
43
|
+
'.impeccable/live/manual-edit-events.jsonl',
|
|
44
|
+
'.impeccable/live/manual-edit-evidence/',
|
|
45
|
+
'.impeccable/live/pending-manual-edits.json',
|
|
46
|
+
'.impeccable/live/deferred-svelte-component-accepts.json',
|
|
47
|
+
'.impeccable-live.json',
|
|
48
|
+
'.impeccable-live/',
|
|
49
|
+
'node_modules/.impeccable-live/',
|
|
50
|
+
'src/lib/impeccable/ImpeccableLiveRoot.svelte',
|
|
51
|
+
'src/lib/impeccable/__runtime.js',
|
|
52
|
+
'src/lib/impeccable/[0-9a-f]*/',
|
|
53
|
+
]);
|
|
25
54
|
|
|
26
55
|
/**
|
|
27
56
|
* Hard-excluded directory patterns. These are NEVER user-facing pages and
|
|
@@ -83,8 +112,14 @@ Output (JSON):
|
|
|
83
112
|
validateConfig(config);
|
|
84
113
|
|
|
85
114
|
const resolvedFiles = resolveFiles(process.cwd(), config);
|
|
115
|
+
const svelteKit = detectSvelteKitProject(process.cwd(), config);
|
|
86
116
|
|
|
87
117
|
if (args.includes('--remove')) {
|
|
118
|
+
if (svelteKit) {
|
|
119
|
+
const adapterResult = removeSvelteKitLiveAdapter({ cwd: process.cwd(), config });
|
|
120
|
+
console.log(JSON.stringify({ ok: true, adapter: 'sveltekit', results: [adapterResult] }));
|
|
121
|
+
return;
|
|
122
|
+
}
|
|
88
123
|
const results = resolvedFiles.map((relFile) => {
|
|
89
124
|
const absFile = path.resolve(process.cwd(), relFile);
|
|
90
125
|
if (!fs.existsSync(absFile)) return { file: relFile, error: 'file_not_found' };
|
|
@@ -110,6 +145,13 @@ Output (JSON):
|
|
|
110
145
|
console.error(JSON.stringify({ ok: false, error: 'missing_port' }));
|
|
111
146
|
process.exit(1);
|
|
112
147
|
}
|
|
148
|
+
const gitIgnore = ensureLiveGitIgnores(process.cwd());
|
|
149
|
+
|
|
150
|
+
if (svelteKit) {
|
|
151
|
+
const adapterResult = applySvelteKitLiveAdapter({ cwd: process.cwd(), port, config });
|
|
152
|
+
console.log(JSON.stringify({ ok: true, port, adapter: 'sveltekit', gitIgnore, results: [adapterResult] }));
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
113
155
|
|
|
114
156
|
const results = resolvedFiles.map((relFile) => {
|
|
115
157
|
const absFile = path.resolve(process.cwd(), relFile);
|
|
@@ -129,10 +171,68 @@ Output (JSON):
|
|
|
129
171
|
};
|
|
130
172
|
});
|
|
131
173
|
const anyInserted = results.some((r) => r.inserted);
|
|
132
|
-
console.log(JSON.stringify({ ok: anyInserted, port, results }));
|
|
174
|
+
console.log(JSON.stringify({ ok: anyInserted, port, gitIgnore, results }));
|
|
133
175
|
if (!anyInserted) process.exit(1);
|
|
134
176
|
}
|
|
135
177
|
|
|
178
|
+
export function ensureLiveGitIgnores(cwd = process.cwd()) {
|
|
179
|
+
const target = resolveIgnoreTarget(cwd);
|
|
180
|
+
const existing = fs.existsSync(target.path) ? fs.readFileSync(target.path, 'utf-8') : '';
|
|
181
|
+
const block = [
|
|
182
|
+
IGNORE_MARKER_OPEN,
|
|
183
|
+
...LIVE_IGNORE_PATTERNS,
|
|
184
|
+
IGNORE_MARKER_CLOSE,
|
|
185
|
+
].join('\n');
|
|
186
|
+
const markerRe = new RegExp(`${escapeRegExp(IGNORE_MARKER_OPEN)}[\\s\\S]*?${escapeRegExp(IGNORE_MARKER_CLOSE)}`);
|
|
187
|
+
|
|
188
|
+
let updated;
|
|
189
|
+
if (markerRe.test(existing)) {
|
|
190
|
+
updated = existing.replace(markerRe, block);
|
|
191
|
+
} else {
|
|
192
|
+
const prefix = existing.length === 0 ? '' : existing.endsWith('\n') ? existing : existing + '\n';
|
|
193
|
+
updated = `${prefix}${prefix.endsWith('\n\n') || prefix === '' ? '' : '\n'}${block}\n`;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
if (updated !== existing) {
|
|
197
|
+
fs.mkdirSync(path.dirname(target.path), { recursive: true });
|
|
198
|
+
fs.writeFileSync(target.path, updated, 'utf-8');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
return {
|
|
202
|
+
file: path.relative(cwd, target.path).split(path.sep).join('/'),
|
|
203
|
+
mode: target.mode,
|
|
204
|
+
changed: updated !== existing,
|
|
205
|
+
patterns: [...LIVE_IGNORE_PATTERNS],
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
function resolveIgnoreTarget(cwd) {
|
|
210
|
+
const gitExcludePath = resolveGitInfoExcludePath(cwd);
|
|
211
|
+
if (gitExcludePath) {
|
|
212
|
+
return { path: gitExcludePath, mode: 'git-info-exclude' };
|
|
213
|
+
}
|
|
214
|
+
return { path: path.join(cwd, '.gitignore'), mode: 'gitignore' };
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function resolveGitInfoExcludePath(cwd) {
|
|
218
|
+
const dotGit = path.join(cwd, '.git');
|
|
219
|
+
if (!fs.existsSync(dotGit)) return null;
|
|
220
|
+
|
|
221
|
+
const stat = fs.statSync(dotGit);
|
|
222
|
+
if (stat.isDirectory()) return path.join(dotGit, 'info', 'exclude');
|
|
223
|
+
if (!stat.isFile()) return null;
|
|
224
|
+
|
|
225
|
+
const body = fs.readFileSync(dotGit, 'utf-8').trim();
|
|
226
|
+
const match = body.match(/^gitdir:\s*(.+)$/i);
|
|
227
|
+
if (!match) return null;
|
|
228
|
+
const gitDir = path.isAbsolute(match[1]) ? match[1] : path.resolve(cwd, match[1]);
|
|
229
|
+
return path.join(gitDir, 'info', 'exclude');
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
function escapeRegExp(value) {
|
|
233
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
234
|
+
}
|
|
235
|
+
|
|
136
236
|
/**
|
|
137
237
|
* Expand config.files (which may contain glob patterns) into a literal list
|
|
138
238
|
* of existing file paths relative to rootDir. Literal entries pass through;
|
|
@@ -270,8 +370,26 @@ function buildTagBlock(syntax, port, filePath) {
|
|
|
270
370
|
);
|
|
271
371
|
}
|
|
272
372
|
|
|
373
|
+
function detectLineEnding(content) {
|
|
374
|
+
if (content.includes('\r\n')) return '\r\n';
|
|
375
|
+
if (content.includes('\r')) return '\r';
|
|
376
|
+
return '\n';
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
function normalizeLineEndings(content, lineEnding) {
|
|
380
|
+
return lineEnding === '\n' ? content : content.replace(/\n/g, lineEnding);
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function readLineEndingAt(content, index) {
|
|
384
|
+
if (content[index] === '\r' && content[index + 1] === '\n') return '\r\n';
|
|
385
|
+
if (content[index] === '\n') return '\n';
|
|
386
|
+
if (content[index] === '\r') return '\r';
|
|
387
|
+
return '';
|
|
388
|
+
}
|
|
389
|
+
|
|
273
390
|
function insertTag(content, config, port, filePath) {
|
|
274
|
-
const
|
|
391
|
+
const lineEnding = detectLineEnding(content);
|
|
392
|
+
const block = normalizeLineEndings(buildTagBlock(config.commentSyntax, port, filePath), lineEnding);
|
|
275
393
|
// insertBefore: match the LAST occurrence. Anchors like `</body>` naturally
|
|
276
394
|
// belong at the end, and the same literal can appear earlier in code blocks
|
|
277
395
|
// within rendered documentation pages.
|
|
@@ -285,9 +403,15 @@ function insertTag(content, config, port, filePath) {
|
|
|
285
403
|
const idx = content.indexOf(config.insertAfter);
|
|
286
404
|
if (idx === -1) return content;
|
|
287
405
|
const after = idx + config.insertAfter.length;
|
|
288
|
-
// Preserve
|
|
289
|
-
|
|
290
|
-
|
|
406
|
+
// Preserve an existing trailing newline if the anchor already has one.
|
|
407
|
+
// Slice the remainder from the original anchor offset, not prefix.length:
|
|
408
|
+
// in the no-newline case prefix is one char longer than the anchor (the
|
|
409
|
+
// appended '\n'), so slicing by prefix.length would drop the first real
|
|
410
|
+
// character after the anchor (#227).
|
|
411
|
+
const existingNewline = readLineEndingAt(content, after);
|
|
412
|
+
const prefix = content.slice(0, after) + (existingNewline || lineEnding);
|
|
413
|
+
const rest = content.slice(after + existingNewline.length);
|
|
414
|
+
return prefix + block + rest;
|
|
291
415
|
}
|
|
292
416
|
|
|
293
417
|
/**
|
|
@@ -303,8 +427,8 @@ function insertTag(content, config, port, filePath) {
|
|
|
303
427
|
*/
|
|
304
428
|
function removeTag(content, _syntax) {
|
|
305
429
|
const patterns = [
|
|
306
|
-
/([ \t]*)<!--\s*impeccable-live-start\s*-->[\s\S]*?<!--\s*impeccable-live-end\s*-->([ \t]*(?:\n|$)?)/,
|
|
307
|
-
/([ \t]*)\{\/\*\s*impeccable-live-start\s*\*\/\}[\s\S]*?\{\/\*\s*impeccable-live-end\s*\*\/\}([ \t]*(?:\n|$)?)/,
|
|
430
|
+
/([ \t]*)<!--\s*impeccable-live-start\s*-->[\s\S]*?<!--\s*impeccable-live-end\s*-->([ \t]*(?:\r\n|\n|\r|$)?)/,
|
|
431
|
+
/([ \t]*)\{\/\*\s*impeccable-live-start\s*\*\/\}[\s\S]*?\{\/\*\s*impeccable-live-end\s*\*\/\}([ \t]*(?:\r\n|\n|\r|$)?)/,
|
|
308
432
|
];
|
|
309
433
|
for (const pat of patterns) {
|
|
310
434
|
let changed = false;
|
|
@@ -312,7 +436,7 @@ function removeTag(content, _syntax) {
|
|
|
312
436
|
do {
|
|
313
437
|
content = next;
|
|
314
438
|
next = content.replace(pat, (_match, leadingIndent, trailing = '') => {
|
|
315
|
-
if (
|
|
439
|
+
if (/[\r\n]/.test(trailing)) return leadingIndent;
|
|
316
440
|
return leadingIndent || trailing || '';
|
|
317
441
|
});
|
|
318
442
|
if (next !== content) changed = true;
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
|
|
10
10
|
import fs from 'node:fs';
|
|
11
11
|
import path from 'node:path';
|
|
12
|
-
import { isGeneratedFile } from './is-generated.mjs';
|
|
12
|
+
import { isGeneratedFile } from './lib/is-generated.mjs';
|
|
13
13
|
import {
|
|
14
14
|
buildSearchQueries,
|
|
15
15
|
findElement,
|
|
@@ -21,6 +21,11 @@ import {
|
|
|
21
21
|
buildCssAuthoring,
|
|
22
22
|
buildCssSelectorPrefixExamples,
|
|
23
23
|
} from './live-wrap.mjs';
|
|
24
|
+
import {
|
|
25
|
+
buildSvelteComponentCssAuthoring,
|
|
26
|
+
scaffoldSvelteComponentInsertSession,
|
|
27
|
+
shouldUseSvelteComponentInjection,
|
|
28
|
+
} from './live/svelte-component.mjs';
|
|
24
29
|
|
|
25
30
|
const INSERT_POSITIONS = new Set(['before', 'after']);
|
|
26
31
|
|
|
@@ -192,6 +197,41 @@ Output (JSON):
|
|
|
192
197
|
const styleMode = detectStyleMode(targetFile);
|
|
193
198
|
const isJsx = commentSyntax.open === '{/*';
|
|
194
199
|
const spliceIndex = computeInsertLine(startLine, endLine, position);
|
|
200
|
+
const relTargetFile = path.relative(process.cwd(), targetFile).split(path.sep).join('/');
|
|
201
|
+
|
|
202
|
+
if (shouldUseSvelteComponentInjection(targetFile)) {
|
|
203
|
+
const session = scaffoldSvelteComponentInsertSession({
|
|
204
|
+
id,
|
|
205
|
+
count,
|
|
206
|
+
sourceFile: relTargetFile,
|
|
207
|
+
insertLine: spliceIndex + 1,
|
|
208
|
+
position,
|
|
209
|
+
anchorStartLine: startLine + 1,
|
|
210
|
+
anchorEndLine: endLine + 1,
|
|
211
|
+
anchorLines: lines.slice(startLine, endLine + 1),
|
|
212
|
+
cwd: process.cwd(),
|
|
213
|
+
});
|
|
214
|
+
console.log(JSON.stringify({
|
|
215
|
+
mode: 'insert',
|
|
216
|
+
position,
|
|
217
|
+
file: session.manifestFile,
|
|
218
|
+
sourceFile: relTargetFile,
|
|
219
|
+
previewMode: 'svelte-component',
|
|
220
|
+
componentDir: session.componentDir,
|
|
221
|
+
propContract: session.propContract,
|
|
222
|
+
insertLine: 1,
|
|
223
|
+
sourceInsertLine: spliceIndex + 1,
|
|
224
|
+
anchorStartLine: startLine + 1,
|
|
225
|
+
anchorEndLine: endLine + 1,
|
|
226
|
+
commentSyntax,
|
|
227
|
+
styleMode: 'svelte-component',
|
|
228
|
+
styleTag: null,
|
|
229
|
+
cssSelectorPrefixExamples: [],
|
|
230
|
+
cssAuthoring: buildSvelteComponentCssAuthoring(count),
|
|
231
|
+
}));
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
195
235
|
const indent = lines[spliceIndex]?.match(/^(\s*)/)?.[1]
|
|
196
236
|
?? lines[startLine]?.match(/^(\s*)/)?.[1]
|
|
197
237
|
?? '';
|
|
@@ -216,7 +256,7 @@ Output (JSON):
|
|
|
216
256
|
console.log(JSON.stringify({
|
|
217
257
|
mode: 'insert',
|
|
218
258
|
position,
|
|
219
|
-
file:
|
|
259
|
+
file: relTargetFile,
|
|
220
260
|
insertLine: insertLine + 1,
|
|
221
261
|
commentSyntax,
|
|
222
262
|
styleMode: styleMode.mode,
|
|
@@ -10,8 +10,8 @@
|
|
|
10
10
|
|
|
11
11
|
import fs from 'node:fs';
|
|
12
12
|
import path from 'node:path';
|
|
13
|
-
import { isGeneratedFile } from './is-generated.mjs';
|
|
14
|
-
import { readBuffer, getBufferPath } from './live
|
|
13
|
+
import { isGeneratedFile } from './lib/is-generated.mjs';
|
|
14
|
+
import { readBuffer, getBufferPath } from './live/manual-edits-buffer.mjs';
|
|
15
15
|
|
|
16
16
|
const EVIDENCE_VERSION = 1;
|
|
17
17
|
const TEXT_EXTENSIONS = new Set(['.html', '.jsx', '.tsx', '.vue', '.svelte', '.astro', '.js', '.mjs', '.ts']);
|
|
@@ -353,9 +353,9 @@ function decodeBasicHtml(value) {
|
|
|
353
353
|
.replace(/"/g, '"')
|
|
354
354
|
.replace(/'/g, "'")
|
|
355
355
|
.replace(/'/g, "'")
|
|
356
|
+
.replace(/&/g, '&')
|
|
356
357
|
.replace(/</g, '<')
|
|
357
|
-
.replace(/>/g, '>')
|
|
358
|
-
.replace(/&/g, '&');
|
|
358
|
+
.replace(/>/g, '>');
|
|
359
359
|
}
|
|
360
360
|
|
|
361
361
|
function escapeRegExp(value) {
|
|
@@ -2,31 +2,37 @@
|
|
|
2
2
|
* CLI client for the live variant mode poll/reply protocol.
|
|
3
3
|
*
|
|
4
4
|
* Usage:
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
5
|
+
* node <scripts_path>/live-poll.mjs # Block until browser event, print JSON
|
|
6
|
+
* node <scripts_path>/live-poll.mjs --stream # Experimental: keep polling; one JSON line per event
|
|
7
|
+
* node <scripts_path>/live-poll.mjs --timeout=600000 # Custom timeout (ms); default is long-poll friendly
|
|
8
|
+
* node <scripts_path>/live-poll.mjs --reply <id> done # Reply "done" to event <id>
|
|
9
|
+
* node <scripts_path>/live-poll.mjs --reply <id> error "msg" # Reply with error
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
12
|
import { execFileSync } from 'node:child_process';
|
|
13
13
|
import path from 'node:path';
|
|
14
14
|
import { fileURLToPath } from 'node:url';
|
|
15
|
-
import { completionAckForAcceptResult, completionTypeForAcceptResult } from './live
|
|
16
|
-
import { readLiveServerInfo } from './impeccable-paths.mjs';
|
|
15
|
+
import { completionAckForAcceptResult, completionTypeForAcceptResult } from './live/completion.mjs';
|
|
16
|
+
import { readLiveServerInfo } from './lib/impeccable-paths.mjs';
|
|
17
|
+
|
|
18
|
+
// Absolute path to a sibling script in this skill's scripts dir, so runtime
|
|
19
|
+
// error hints print a directly-runnable command instead of a placeholder.
|
|
20
|
+
const SELF_DIR = path.dirname(fileURLToPath(import.meta.url));
|
|
21
|
+
const scriptCmd = (name) => `node "${path.join(SELF_DIR, name)}"`;
|
|
17
22
|
|
|
18
23
|
// Node's built-in fetch (undici under the hood) enforces a 300s headers
|
|
19
24
|
// timeout that can't be lowered per-request. We cap each request below
|
|
20
25
|
// that ceiling and loop in `pollOnce` to synthesize a long poll without
|
|
21
26
|
// depending on the standalone undici package.
|
|
22
27
|
export const PER_REQUEST_TIMEOUT_MS = 270_000;
|
|
28
|
+
export const DEFAULT_EVENT_LEASE_MS = 600_000;
|
|
23
29
|
|
|
24
30
|
const EVENT_TYPES_NEEDING_AGENT_REPLY = new Set(['generate', 'steer', 'manual_edit_apply']);
|
|
25
31
|
|
|
26
32
|
function readServerInfo() {
|
|
27
33
|
const record = readLiveServerInfo(process.cwd());
|
|
28
34
|
if (!record) {
|
|
29
|
-
console.error(
|
|
35
|
+
console.error(`No running live server found. Start one with: ${scriptCmd('live.mjs')}`);
|
|
30
36
|
process.exit(1);
|
|
31
37
|
}
|
|
32
38
|
return record.info;
|
|
@@ -81,7 +87,7 @@ export function parseReplyArgs(args) {
|
|
|
81
87
|
}
|
|
82
88
|
|
|
83
89
|
function validateReplyArgs({ id, status }) {
|
|
84
|
-
const usage =
|
|
90
|
+
const usage = `Usage: ${scriptCmd('live-poll.mjs')} --reply <id> <status> [--file path] [--data '<json>'] [message]`;
|
|
85
91
|
if (!id || id.startsWith('--')) {
|
|
86
92
|
const err = new Error(`${usage}\nMissing event id after --reply.`);
|
|
87
93
|
err.code = 'INVALID_REPLY_ARGS';
|
|
@@ -156,7 +162,7 @@ export async function fetchNextEvent(base, token, { totalDeadline } = {}) {
|
|
|
156
162
|
? totalDeadline - Date.now()
|
|
157
163
|
: PER_REQUEST_TIMEOUT_MS;
|
|
158
164
|
const slice = Math.min(Math.max(remaining, 1000), PER_REQUEST_TIMEOUT_MS);
|
|
159
|
-
const res = await fetch(`${base}/poll?token=${token}&timeout=${slice}`);
|
|
165
|
+
const res = await fetch(`${base}/poll?token=${token}&timeout=${slice}&leaseMs=${DEFAULT_EVENT_LEASE_MS}`);
|
|
160
166
|
|
|
161
167
|
if (res.status === 401) {
|
|
162
168
|
const err = new Error('Authentication failed. The server token may have changed.');
|
|
@@ -282,11 +288,11 @@ export async function runPollStream(base, token, {
|
|
|
282
288
|
function handlePollError(err) {
|
|
283
289
|
if (err.code === 'AUTH_FAILED') {
|
|
284
290
|
console.error(err.message);
|
|
285
|
-
console.error(
|
|
291
|
+
console.error(`Try restarting: ${scriptCmd('live-server.mjs')} stop && ${scriptCmd('live.mjs')}`);
|
|
286
292
|
process.exit(1);
|
|
287
293
|
}
|
|
288
294
|
if (err.cause?.code === 'ECONNREFUSED') {
|
|
289
|
-
console.error(
|
|
295
|
+
console.error(`Live server not running. Start one with: ${scriptCmd('live.mjs')}`);
|
|
290
296
|
process.exit(1);
|
|
291
297
|
}
|
|
292
298
|
if (err.code === 'ACK_TIMEOUT') {
|
|
@@ -317,7 +323,7 @@ Modes:
|
|
|
317
323
|
Options:
|
|
318
324
|
--timeout=MS One-shot poll timeout in ms (default: 600000). Ignored in --stream mode
|
|
319
325
|
--ack-timeout=MS Stream mode: max wait for --reply after generate/steer (default: 600000)
|
|
320
|
-
--file PATH Attach a source file path to the reply (generate flow)
|
|
326
|
+
--file PATH Attach a source file path to the reply (generate/steer flow)
|
|
321
327
|
--data JSON Attach a JSON result object to the reply (manual_edit_apply flow). Must be valid JSON
|
|
322
328
|
--help Show this help message
|
|
323
329
|
|
|
@@ -330,7 +336,7 @@ Harness note:
|
|
|
330
336
|
const info = readServerInfo();
|
|
331
337
|
const base = `http://localhost:${info.port}`;
|
|
332
338
|
|
|
333
|
-
// Reply mode:
|
|
339
|
+
// Reply mode: node <scripts_path>/live-poll.mjs --reply <id> <status> [--file path] [--data '<json>'] [message]
|
|
334
340
|
if (args.includes('--reply')) {
|
|
335
341
|
let reply;
|
|
336
342
|
try {
|
|
@@ -344,7 +350,7 @@ Harness note:
|
|
|
344
350
|
await postReply(base, info.token, reply);
|
|
345
351
|
} catch (err) {
|
|
346
352
|
if (err.cause?.code === 'ECONNREFUSED') {
|
|
347
|
-
console.error(
|
|
353
|
+
console.error(`Live server not running. Start one with: ${scriptCmd('live.mjs')}`);
|
|
348
354
|
} else {
|
|
349
355
|
console.error('Reply failed:', err.message);
|
|
350
356
|
}
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
* Recover the next agent action from the durable live-session journal.
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
-
import { createLiveSessionStore } from './live
|
|
6
|
+
import { createLiveSessionStore } from './live/session-store.mjs';
|
|
7
7
|
|
|
8
8
|
function manualApplyReplyCommand(eventOrId = 'EVENT_ID') {
|
|
9
9
|
const id = typeof eventOrId === 'string' ? eventOrId : eventOrId?.id || 'EVENT_ID';
|