@jini-ai/vibecoding 0.6.15-rc.2 → 0.6.16
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 +12 -0
- package/dist/core/apply.d.ts +0 -11
- package/dist/core/apply.js +9 -6
- package/dist/html/edit.js +17 -1
- package/dist/html/node/parse5-region-parser.d.ts +3 -1
- package/dist/html/node/parse5-region-parser.js +11 -8
- package/dist/html/regions.js +9 -5
- package/dist/node/paths.js +2 -1
- package/dist/node/project.js +13 -13
- package/dist/node/store.js +28 -9
- package/dist/react/components/PartsViewer.d.ts +0 -6
- package/dist/react/components/PartsViewer.js +10 -1
- package/dist/react/components/VibecodingWorkbench.js +1 -1
- package/dist/react/index.d.ts +1 -1
- package/package.json +10 -10
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.6.16
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Remove the GitHub Copilot CLI agent: its def, `createCopilotStreamHandler`/`CopilotStreamEvent`,
|
|
8
|
+
account model discovery and the `@github/copilot-sdk` optional peer, the MCP installer's `copilot`
|
|
9
|
+
slug, and its icon. A saved `copilot` agent id no longer resolves (`getAgentDef` returns `null`).
|
|
10
|
+
CopilotKit/AG-UI is unrelated and unchanged.
|
|
11
|
+
|
|
12
|
+
Drop peers no runtime entry loads: `express` from core and http-kit (type-only imports; type
|
|
13
|
+
consumers keep `@types/express`), `vue` from ui-kit, `react-dom` from vibecoding.
|
|
14
|
+
|
|
3
15
|
## 0.6.13
|
|
4
16
|
|
|
5
17
|
### Patch Changes
|
package/dist/core/apply.d.ts
CHANGED
|
@@ -49,17 +49,6 @@ export declare function applyEdits({ target, edits }: {
|
|
|
49
49
|
readonly target: EditTarget;
|
|
50
50
|
readonly edits: readonly ProposedEdit[];
|
|
51
51
|
}): Promise<readonly ApplyOutcome[]>;
|
|
52
|
-
/**
|
|
53
|
-
* The subset of outcomes a host should feed back to the model as its next turn.
|
|
54
|
-
*
|
|
55
|
-
* Rejections carry a reason written for the model; write failures carry an error message. Both are
|
|
56
|
-
* things the model can act on, and neither reaches it unless the host asks for them here — which
|
|
57
|
-
* is what closes the loop between validation and correction.
|
|
58
|
-
*
|
|
59
|
-
* @param requiredArgs - results from `applyEdits`.
|
|
60
|
-
* @returns one `{ id, reason }` per non-applied outcome, in input order.
|
|
61
|
-
* @complexity 3
|
|
62
|
-
*/
|
|
63
52
|
export declare function correctionsFor({ outcomes }: {
|
|
64
53
|
readonly outcomes: readonly ApplyOutcome[];
|
|
65
54
|
}): readonly {
|
package/dist/core/apply.js
CHANGED
|
@@ -54,15 +54,18 @@ export async function applyEdits({ target, edits }) {
|
|
|
54
54
|
* @returns one `{ id, reason }` per non-applied outcome, in input order.
|
|
55
55
|
* @complexity 3
|
|
56
56
|
*/
|
|
57
|
+
/** Why an outcome needs correcting; applied outcomes need none. */
|
|
58
|
+
function correctionReason(outcome) {
|
|
59
|
+
if (outcome.status === "rejected")
|
|
60
|
+
return outcome.reason;
|
|
61
|
+
return outcome.status === "failed" ? outcome.error.message : undefined;
|
|
62
|
+
}
|
|
57
63
|
export function correctionsFor({ outcomes }) {
|
|
58
64
|
const corrections = [];
|
|
59
65
|
for (const outcome of outcomes) {
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
else if (outcome.status === "failed") {
|
|
64
|
-
corrections.push({ id: outcome.id, reason: outcome.error.message });
|
|
65
|
-
}
|
|
66
|
+
const reason = correctionReason(outcome);
|
|
67
|
+
if (reason !== undefined)
|
|
68
|
+
corrections.push({ id: outcome.id, reason });
|
|
66
69
|
}
|
|
67
70
|
return corrections;
|
|
68
71
|
}
|
package/dist/html/edit.js
CHANGED
|
@@ -1,3 +1,19 @@
|
|
|
1
|
+
import { trimCharsEnd } from "@jini-ai/core/primitives";
|
|
2
|
+
/** Replaces each `<...>` tag with a space, exactly as `text.replace(/<[^>]*>/g, " ")` does but in
|
|
3
|
+
* linear time: once a `<` has no closing `>` after it, no later `<` has one either. */
|
|
4
|
+
function replaceTags(text) {
|
|
5
|
+
let out = "";
|
|
6
|
+
let from = 0;
|
|
7
|
+
let open = text.indexOf("<");
|
|
8
|
+
let close = open === -1 ? -1 : text.indexOf(">", open + 1);
|
|
9
|
+
while (close !== -1) {
|
|
10
|
+
out += text.slice(from, open) + " ";
|
|
11
|
+
from = close + 1;
|
|
12
|
+
open = text.indexOf("<", from);
|
|
13
|
+
close = open === -1 ? -1 : text.indexOf(">", open + 1);
|
|
14
|
+
}
|
|
15
|
+
return out + text.slice(from);
|
|
16
|
+
}
|
|
1
17
|
/** Resolve one address, refusing duplicates and unsafe boundaries without throwing on reads. */
|
|
2
18
|
export function locateRegion({ scan, handle }, _options = {}) {
|
|
3
19
|
const matches = scan.regions.filter((region) => region.handle === handle);
|
|
@@ -56,7 +72,7 @@ export function suggestRegionHandle({ inner, taken }, { slugify }) {
|
|
|
56
72
|
const heading = HEADING_PATTERN.exec(inner);
|
|
57
73
|
if (!heading)
|
|
58
74
|
return undefined;
|
|
59
|
-
const slug = slugify(heading[1]
|
|
75
|
+
const slug = trimCharsEnd({ text: slugify(replaceTags(heading[1])).slice(0, 48), chars: "-" });
|
|
60
76
|
if (slug === "" || taken.has(slug))
|
|
61
77
|
return undefined;
|
|
62
78
|
return slug;
|
|
@@ -10,7 +10,9 @@ import { isValidRegionHandle, type HtmlRegionParser } from "../regions.js";
|
|
|
10
10
|
*/
|
|
11
11
|
export declare function scanHtmlRegions({ html: html_ }: {
|
|
12
12
|
readonly html: string;
|
|
13
|
-
}, _options?: Record<string, never>): HtmlRegionScan
|
|
13
|
+
}, _options?: Record<string, never>): HtmlRegionScan & {
|
|
14
|
+
readonly parseErrorReasons: readonly string[];
|
|
15
|
+
};
|
|
14
16
|
/**
|
|
15
17
|
* Build the parse5-backed `HtmlRegionParser`. Stateless — a single instance is safe to share as a
|
|
16
18
|
* module-level singleton across every call, per `./regions.js`'s injected-port contract.
|
|
@@ -104,9 +104,7 @@ function* walkNodes(root) {
|
|
|
104
104
|
while (pending.length > 0) {
|
|
105
105
|
const node = pending.pop(); // Nonempty stack on this branch.
|
|
106
106
|
yield node;
|
|
107
|
-
const children =
|
|
108
|
-
if (children === undefined)
|
|
109
|
-
continue;
|
|
107
|
+
const children = childrenOf(node);
|
|
110
108
|
for (let index = children.length - 1; index >= 0; index -= 1)
|
|
111
109
|
pending.push(children[index]);
|
|
112
110
|
}
|
|
@@ -174,15 +172,19 @@ function analyzeRegion(element, handle) {
|
|
|
174
172
|
...(problem === undefined ? {} : { problem }),
|
|
175
173
|
};
|
|
176
174
|
}
|
|
175
|
+
/** A template's content children, an ordinary node's children, or none for a leaf. */
|
|
176
|
+
function childrenOf(node) {
|
|
177
|
+
if ("content" in node)
|
|
178
|
+
return node.content.childNodes;
|
|
179
|
+
return "childNodes" in node ? node.childNodes : [];
|
|
180
|
+
}
|
|
177
181
|
/** Direct fragment children in source order; implied closes suffice for tagging advice, not edits. */
|
|
178
182
|
function topLevelElements(fragment) {
|
|
179
183
|
const elements = [];
|
|
180
184
|
for (const child of fragment.childNodes) {
|
|
181
|
-
|
|
185
|
+
// Implicit wrappers have no authored open tag to annotate.
|
|
186
|
+
if (!("tagName" in child) || !child.sourceCodeLocation?.startTag)
|
|
182
187
|
continue;
|
|
183
|
-
const location = child.sourceCodeLocation;
|
|
184
|
-
if (!location?.startTag)
|
|
185
|
-
continue; // Implicit wrappers have no authored open tag to annotate.
|
|
186
188
|
const handle = attributeValue(child, AGENT_ELEMENT_ATTRIBUTE);
|
|
187
189
|
elements.push({
|
|
188
190
|
tag: child.tagName,
|
|
@@ -292,7 +294,8 @@ export function createParse5RegionParser() {
|
|
|
292
294
|
const problem = locationProblem(scan);
|
|
293
295
|
if (problem !== undefined)
|
|
294
296
|
return { ok: false, reason: problem };
|
|
295
|
-
|
|
297
|
+
// This concrete scanner always initializes the list, unlike the optional generic port field.
|
|
298
|
+
const parseErrorReasons = scan.parseErrorReasons;
|
|
296
299
|
if (parseErrorReasons.length > 0) {
|
|
297
300
|
return { ok: false, reason: `the document does not parse cleanly: ${parseErrorReasons.join("; ")}` };
|
|
298
301
|
}
|
package/dist/html/regions.js
CHANGED
|
@@ -77,6 +77,14 @@ function handleCounts(regions) {
|
|
|
77
77
|
return counts;
|
|
78
78
|
}
|
|
79
79
|
/** A stable, order-insensitive description of a handle multiset, for comparison in a message. */
|
|
80
|
+
/** Which way an edit disturbed the region's agent handles. */
|
|
81
|
+
function handleChangeDetail({ invented, removed }) {
|
|
82
|
+
if (invented.length > 0)
|
|
83
|
+
return `it adds ${AGENT_ELEMENT_ATTRIBUTE} handles that did not exist (${describeHandles(invented)})`;
|
|
84
|
+
if (removed.length > 0)
|
|
85
|
+
return `it removes existing ${AGENT_ELEMENT_ATTRIBUTE} handles (${describeHandles(removed)})`;
|
|
86
|
+
return `it changes how many times a ${AGENT_ELEMENT_ATTRIBUTE} handle appears`;
|
|
87
|
+
}
|
|
80
88
|
function describeHandles(handles) {
|
|
81
89
|
return [...handles].sort().join(", ") || "(none)";
|
|
82
90
|
}
|
|
@@ -202,11 +210,7 @@ export function createHtmlRegionTarget(deps, options = {}) {
|
|
|
202
210
|
if (beforeKey !== afterKey) {
|
|
203
211
|
const invented = afterHandles.filter((handle) => !beforeHandles.includes(handle));
|
|
204
212
|
const removed = beforeHandles.filter((handle) => !afterHandles.includes(handle));
|
|
205
|
-
const detail = invented
|
|
206
|
-
? `it adds ${AGENT_ELEMENT_ATTRIBUTE} handles that did not exist (${describeHandles(invented)})`
|
|
207
|
-
: removed.length > 0
|
|
208
|
-
? `it removes existing ${AGENT_ELEMENT_ATTRIBUTE} handles (${describeHandles(removed)})`
|
|
209
|
-
: `it changes how many times a ${AGENT_ELEMENT_ATTRIBUTE} handle appears`;
|
|
213
|
+
const detail = handleChangeDetail({ invented, removed });
|
|
210
214
|
return {
|
|
211
215
|
ok: false,
|
|
212
216
|
reason: `${detail}. Edit only the content inside the region you were asked to change, and do not add, remove or duplicate ${AGENT_ELEMENT_ATTRIBUTE} attributes.`,
|
package/dist/node/paths.js
CHANGED
|
@@ -41,7 +41,8 @@ export function canonicalPath({ relativePath, maxDepth }, _options = {}) {
|
|
|
41
41
|
}
|
|
42
42
|
/** Each segment must survive Windows parsing without aliases, streams or device semantics. */
|
|
43
43
|
function assertSegment({ segment }) {
|
|
44
|
-
|
|
44
|
+
const reservedName = !segment || segment === '.' || segment === '..';
|
|
45
|
+
if (reservedName || /[<>:"|?*\x00-\x1f\x7f]/.test(segment) || /[. ]$/.test(segment) || DEVICE.test(segment)) {
|
|
45
46
|
throw new ProjectError({ code: 'INVALID_PATH', message: `nonportable path segment: ${segment}` });
|
|
46
47
|
}
|
|
47
48
|
}
|
package/dist/node/project.js
CHANGED
|
@@ -29,11 +29,9 @@ function changeFiles({ files: base, changes, paths, limits }) {
|
|
|
29
29
|
return files;
|
|
30
30
|
}
|
|
31
31
|
function changeFile({ files, change, path, limits }) {
|
|
32
|
-
if (change.op === 'create')
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
}
|
|
36
|
-
else if (!files.has(path))
|
|
32
|
+
if (change.op === 'create' && files.has(path))
|
|
33
|
+
fail('INVALID_CHANGE', `file already exists: ${path}`);
|
|
34
|
+
if (change.op !== 'create' && !files.has(path))
|
|
37
35
|
fail('INVALID_CHANGE', `file not listed: ${path}`);
|
|
38
36
|
if (change.op === 'delete') {
|
|
39
37
|
files.delete(path);
|
|
@@ -300,15 +298,17 @@ function assembleProject({ store, paths, limits, historyLimit, validator }) {
|
|
|
300
298
|
undo: () => replay('undo'), redo: () => replay('redo'), restore: restoreText,
|
|
301
299
|
entries: rawHistory.entries, retainedEntries: rawHistory.retainedEntries, clear: rawHistory.clear,
|
|
302
300
|
};
|
|
301
|
+
const listPartsLocked = async () => [...(await captureLocked()).keys()].sort().map(id => ({ id }));
|
|
302
|
+
const readPartLocked = async (id) => {
|
|
303
|
+
paths.check({ relativePath: id });
|
|
304
|
+
const manifest = await store.manifest({ revisionId: selected().revision });
|
|
305
|
+
if (!manifest.files.some(file => file.path === id))
|
|
306
|
+
fail('INVALID_CHANGE', `file not listed: ${id}`);
|
|
307
|
+
return decodeText({ bytes: await store.readWorkingFile({ relativePath: id }) });
|
|
308
|
+
};
|
|
303
309
|
const target = {
|
|
304
|
-
listParts: () => serial(() => locked(
|
|
305
|
-
readPart: ({ id }) => serial(() => locked(
|
|
306
|
-
paths.check({ relativePath: id });
|
|
307
|
-
const manifest = await store.manifest({ revisionId: selected().revision });
|
|
308
|
-
if (!manifest.files.some(file => file.path === id))
|
|
309
|
-
fail('INVALID_CHANGE', `file not listed: ${id}`);
|
|
310
|
-
return decodeText({ bytes: await store.readWorkingFile({ relativePath: id }) });
|
|
311
|
-
})),
|
|
310
|
+
listParts: () => serial(() => locked(listPartsLocked)),
|
|
311
|
+
readPart: ({ id }) => serial(() => locked(() => readPartLocked(id))),
|
|
312
312
|
replacePart: async (edit) => {
|
|
313
313
|
const outcomes = await applyEdits({ edits: [edit] });
|
|
314
314
|
const outcome = outcomes[0];
|
package/dist/node/store.js
CHANGED
|
@@ -35,12 +35,18 @@ function assertHash(hash) { if (!HASH.test(hash))
|
|
|
35
35
|
function missing(error) { return error?.code === 'ENOENT'; }
|
|
36
36
|
/** Absent destinations (or a file where a parent folder was expected) are not errors here. */
|
|
37
37
|
async function lstatIfPresent(path) {
|
|
38
|
-
return fs.lstat(path).catch(error => {
|
|
39
|
-
|
|
38
|
+
return fs.lstat(path).catch(error => {
|
|
39
|
+
if (missing(error) || error.code === 'ENOTDIR')
|
|
40
|
+
return undefined;
|
|
41
|
+
throw error;
|
|
42
|
+
});
|
|
40
43
|
}
|
|
41
44
|
async function statIfPresent(path) {
|
|
42
|
-
return fs.stat(path).catch(error => {
|
|
43
|
-
|
|
45
|
+
return fs.stat(path).catch(error => {
|
|
46
|
+
if (missing(error))
|
|
47
|
+
return undefined;
|
|
48
|
+
throw error;
|
|
49
|
+
});
|
|
44
50
|
}
|
|
45
51
|
/** Reject links and special files even when platform containment permits an in-root alias.
|
|
46
52
|
* Host owns the directory; this recheck is not a sandbox against hostile local processes.
|
|
@@ -111,12 +117,22 @@ function checkManifest({ value, paths, limits }) {
|
|
|
111
117
|
assertCandidatePaths({ paths: [...seen] });
|
|
112
118
|
return manifest;
|
|
113
119
|
}
|
|
120
|
+
/** A safe, non-negative integer no larger than `max` (a stored size is untrusted data). */
|
|
121
|
+
function isByteCount(value, max = Number.POSITIVE_INFINITY) {
|
|
122
|
+
return Number.isSafeInteger(value) && value >= 0 && value <= max;
|
|
123
|
+
}
|
|
124
|
+
/** Code-unit order, independent of locale. */
|
|
125
|
+
function compareCodeUnits(a, b) {
|
|
126
|
+
if (a === b)
|
|
127
|
+
return 0;
|
|
128
|
+
return a < b ? -1 : 1;
|
|
129
|
+
}
|
|
114
130
|
function checkManifestFile({ file, paths, limits }) {
|
|
115
131
|
if (!file || typeof file.path !== 'string' || typeof file.hash !== 'string')
|
|
116
132
|
corrupt('invalid manifest file');
|
|
117
133
|
assertHash(file.hash);
|
|
118
134
|
paths.check({ relativePath: file.path });
|
|
119
|
-
if (!
|
|
135
|
+
if (!isByteCount(file.size, limits.maxFileBytes))
|
|
120
136
|
corrupt('invalid manifest size');
|
|
121
137
|
}
|
|
122
138
|
function checkCurrent({ value, root }) {
|
|
@@ -139,7 +155,7 @@ function checkObserved(observed) {
|
|
|
139
155
|
function checkFingerprint(value) {
|
|
140
156
|
if (!value || typeof value !== 'object')
|
|
141
157
|
corrupt('invalid file fingerprint');
|
|
142
|
-
if (!
|
|
158
|
+
if (!isByteCount(value.size))
|
|
143
159
|
corrupt('invalid fingerprint size');
|
|
144
160
|
if (!Number.isFinite(value.mtimeMs) || !Number.isFinite(value.ctimeMs))
|
|
145
161
|
corrupt('invalid fingerprint timestamps');
|
|
@@ -209,7 +225,7 @@ export function createProjectStore({ root, stateDir, paths, limits, filesystem }
|
|
|
209
225
|
async function prepare({ files }) {
|
|
210
226
|
assertCandidatePaths({ paths: [...files.keys()] });
|
|
211
227
|
const entries = [];
|
|
212
|
-
for (const [path, bytes] of [...files].sort(([a], [b]) => a
|
|
228
|
+
for (const [path, bytes] of [...files].sort(([a], [b]) => compareCodeUnits(a, b))) {
|
|
213
229
|
const file = { path, hash: digest({ bytes }), size: bytes.byteLength };
|
|
214
230
|
entries.push(file);
|
|
215
231
|
}
|
|
@@ -464,8 +480,11 @@ export function createProjectStore({ root, stateDir, paths, limits, filesystem }
|
|
|
464
480
|
}
|
|
465
481
|
/** Generated orphan objects/manifests are safe to discard only while holding the project lock. */
|
|
466
482
|
async function removeUnreferenced({ directory, keep }) {
|
|
467
|
-
const names = await fs.readdir(directory).catch(error => {
|
|
468
|
-
|
|
483
|
+
const names = await fs.readdir(directory).catch(error => {
|
|
484
|
+
if (missing(error))
|
|
485
|
+
return [];
|
|
486
|
+
throw error;
|
|
487
|
+
});
|
|
469
488
|
for (const name of names) {
|
|
470
489
|
if (!keep.has(name))
|
|
471
490
|
await fs.rm(resolve(directory, name), { force: true });
|
|
@@ -39,11 +39,5 @@ export interface PartsViewerProps {
|
|
|
39
39
|
readonly className?: string;
|
|
40
40
|
readonly style?: CSSProperties;
|
|
41
41
|
}
|
|
42
|
-
/**
|
|
43
|
-
* A flat, selectable list of `parts` on the left and the selected one's raw content on the right.
|
|
44
|
-
*
|
|
45
|
-
* @complexity O(parts.length) to render the list; content rendering is O(content.length).
|
|
46
|
-
* @overallScore 100/100
|
|
47
|
-
*/
|
|
48
42
|
export declare function PartsViewer({ parts, selectedId, onSelect, content, contentLoading, className, style }: PartsViewerProps): import("react").JSX.Element;
|
|
49
43
|
//# sourceMappingURL=PartsViewer.d.ts.map
|
|
@@ -52,10 +52,19 @@ const EMPTY_STYLE = { padding: 12, color: '#6b7280' };
|
|
|
52
52
|
* @complexity O(parts.length) to render the list; content rendering is O(content.length).
|
|
53
53
|
* @overallScore 100/100
|
|
54
54
|
*/
|
|
55
|
+
/** The content pane: a prompt with no selection, a loading note, or the selected part's text. */
|
|
56
|
+
function partContent({ selectedId, contentLoading, content }) {
|
|
57
|
+
if (selectedId === undefined || selectedId === null) {
|
|
58
|
+
return _jsx("p", { style: { ...CONTENT_STYLE, ...EMPTY_STYLE }, children: "Select a part to inspect its content." });
|
|
59
|
+
}
|
|
60
|
+
if (contentLoading)
|
|
61
|
+
return _jsx("p", { style: { ...CONTENT_STYLE, ...EMPTY_STYLE }, children: "Loading\u2026" });
|
|
62
|
+
return _jsx("pre", { style: CONTENT_STYLE, children: content ?? '' });
|
|
63
|
+
}
|
|
55
64
|
export function PartsViewer({ parts, selectedId, onSelect, content, contentLoading, className, style }) {
|
|
56
65
|
return (_jsxs("div", { className: className, style: style ? { ...ROOT_STYLE, ...style } : ROOT_STYLE, children: [_jsx("ul", { style: LIST_STYLE, "aria-label": "Artifact parts", children: parts.length === 0 ? (_jsx("li", { style: EMPTY_STYLE, children: "No parts yet." })) : (parts.map((part) => {
|
|
57
66
|
const isSelected = part.id === selectedId;
|
|
58
67
|
return (_jsx("li", { children: _jsxs("button", { type: "button", onClick: () => onSelect?.(part.id), style: isSelected ? ROW_SELECTED_STYLE : ROW_BASE_STYLE, "aria-current": isSelected, children: [_jsx("span", { style: ROW_LABEL_STYLE, children: part.label ?? part.id }), part.kind ? _jsx("span", { style: ROW_META_STYLE, children: part.kind }) : null] }) }, part.id));
|
|
59
|
-
})) }),
|
|
68
|
+
})) }), partContent({ selectedId, contentLoading, content })] }));
|
|
60
69
|
}
|
|
61
70
|
//# sourceMappingURL=PartsViewer.js.map
|
package/dist/react/index.d.ts
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
* and `VibecodingWorkbench` (both, composed). See this package's README for the three-piece shape
|
|
8
8
|
* this entry point exists to fill in, and each exported module's own doc for its design rationale.
|
|
9
9
|
*
|
|
10
|
-
* React
|
|
10
|
+
* React is an **optional peer dependency**; these components do not load `react-dom` —
|
|
11
11
|
* see this package's `package.json` — so importing `../core` or `../html` alone never pulls React
|
|
12
12
|
* in. Only a consumer of this `./react` subpath needs it installed.
|
|
13
13
|
*/
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@jini-ai/vibecoding",
|
|
3
|
-
"version": "0.6.
|
|
3
|
+
"version": "0.6.16",
|
|
4
4
|
"description": "Conversational artifact authoring: a target-agnostic edit loop — addressable parts, validated replace, operation-level undo/redo, and per-turn snapshot/rewind. Framework-free in `/core`; tagged-region HTML documents behind ./html; filesystem targets behind ./node and the chat/preview surface behind ./react.",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"repository": {
|
|
@@ -20,6 +20,10 @@
|
|
|
20
20
|
"./react": "browser",
|
|
21
21
|
"./session": "universal",
|
|
22
22
|
"./node": "node"
|
|
23
|
+
},
|
|
24
|
+
"optionalPeers": {
|
|
25
|
+
"parse5": "opt-in: html parsing; import @jini-ai/vibecoding/html/node — HTML parsing could not load parse5. Install parse5 to enable this feature, or check its installed version and native binaries.",
|
|
26
|
+
"react": "opt-in: react vibecoding ui; import @jini-ai/vibecoding/react — React vibecoding UI could not load react. Install react to enable this feature, or check its installed version."
|
|
23
27
|
}
|
|
24
28
|
},
|
|
25
29
|
"type": "module",
|
|
@@ -87,25 +91,21 @@
|
|
|
87
91
|
"typescript": "^5.9.2",
|
|
88
92
|
"vitest": "^3.2.4",
|
|
89
93
|
"parse5": "^8.0.1",
|
|
90
|
-
"@jini-ai/
|
|
91
|
-
"@jini-ai/
|
|
94
|
+
"@jini-ai/core": "0.6.16",
|
|
95
|
+
"@jini-ai/platform": "^0.6.16"
|
|
92
96
|
},
|
|
93
97
|
"dependencies": {
|
|
94
|
-
"@jini-ai/core": "^0.6.
|
|
98
|
+
"@jini-ai/core": "^0.6.16"
|
|
95
99
|
},
|
|
96
100
|
"peerDependencies": {
|
|
97
101
|
"react": "^18.3.0 || ^19.0.0",
|
|
98
|
-
"react-dom": "^18.3.0 || ^19.0.0",
|
|
99
102
|
"parse5": "^7.3.0 || ^8.0.1"
|
|
100
103
|
},
|
|
101
104
|
"peerDependenciesMeta": {
|
|
102
|
-
"
|
|
103
|
-
"optional": true
|
|
104
|
-
},
|
|
105
|
-
"react-dom": {
|
|
105
|
+
"parse5": {
|
|
106
106
|
"optional": true
|
|
107
107
|
},
|
|
108
|
-
"
|
|
108
|
+
"react": {
|
|
109
109
|
"optional": true
|
|
110
110
|
}
|
|
111
111
|
},
|