declare-cc 1.0.4 → 1.0.7
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/README.md +90 -5
- package/dist/declare-tools.cjs +10 -6
- package/dist/public/app.js +111 -42
- package/dist/public/index.html +11 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ Run it:
|
|
|
37
37
|
npm run plan
|
|
38
38
|
```
|
|
39
39
|
|
|
40
|
-
This auto-initializes a `.planning/` directory if one doesn't exist, starts the Declare server, writes the port to `.planning/server.port`, and
|
|
40
|
+
This auto-initializes a `.planning/` directory if one doesn't exist, starts the Declare server on a random free port, writes the port to `.planning/server.port`, and prints the dashboard URL.
|
|
41
41
|
|
|
42
42
|
Or run directly:
|
|
43
43
|
|
|
@@ -59,7 +59,7 @@ You declare present-tense statements of fact about your project's future. The sy
|
|
|
59
59
|
|
|
60
60
|
## How It Works
|
|
61
61
|
|
|
62
|
-
Everything happens through the dashboard. `dcl`
|
|
62
|
+
Everything happens through the dashboard. `dcl` starts the server and prints the URL — click it to open.
|
|
63
63
|
|
|
64
64
|
### 1. Declare Futures
|
|
65
65
|
|
|
@@ -125,9 +125,94 @@ Cards are grouped into stages at each level:
|
|
|
125
125
|
| **Ctrl+Shift+A** | Approve all visible |
|
|
126
126
|
| **C** | Command bar |
|
|
127
127
|
|
|
128
|
-
|
|
128
|
+
---
|
|
129
|
+
|
|
130
|
+
## Server & Port Discovery
|
|
131
|
+
|
|
132
|
+
The Declare server uses OS-assigned ports so multiple projects can run simultaneously without collisions.
|
|
133
|
+
|
|
134
|
+
### How it works
|
|
135
|
+
|
|
136
|
+
1. `dcl` (or `dcl serve`) starts the server on **port 0** — the OS assigns a random free port
|
|
137
|
+
2. After the server is listening, it writes the port number to `.planning/server.port` (plain text, e.g. `62964`)
|
|
138
|
+
3. On shutdown (SIGINT, SIGTERM, process exit), the file is deleted
|
|
139
|
+
4. Next time `dcl` runs, it reads the port file. If the server is still alive, it reuses it. If the file is stale (server crashed), it cleans up and starts fresh.
|
|
140
|
+
|
|
141
|
+
### For external tools
|
|
142
|
+
|
|
143
|
+
To discover a running Declare server for a project:
|
|
144
|
+
|
|
145
|
+
```js
|
|
146
|
+
const port = fs.readFileSync('<project>/.planning/server.port', 'utf8').trim();
|
|
147
|
+
// Verify it's alive:
|
|
148
|
+
// GET http://localhost:<port>/api/graph → 200 = running
|
|
149
|
+
// Embed dashboard:
|
|
150
|
+
// <iframe src="http://localhost:<port>/" />
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
If the file doesn't exist, the server isn't running. Start it:
|
|
154
|
+
|
|
155
|
+
```bash
|
|
156
|
+
cd <project> && npx dcl serve
|
|
157
|
+
```
|
|
158
|
+
|
|
159
|
+
Then poll for `.planning/server.port` to appear (~1 second).
|
|
160
|
+
|
|
161
|
+
### Explicit port
|
|
162
|
+
|
|
163
|
+
If you need a specific port:
|
|
164
|
+
|
|
165
|
+
```bash
|
|
166
|
+
dcl serve --port 4000
|
|
167
|
+
```
|
|
168
|
+
|
|
169
|
+
---
|
|
170
|
+
|
|
171
|
+
## For Agents
|
|
172
|
+
|
|
173
|
+
If you're an AI agent (Claude Code, Cursor, etc.) working in a project that uses Declare, here's what you need to know:
|
|
174
|
+
|
|
175
|
+
### Project state lives in `.planning/`
|
|
176
|
+
|
|
177
|
+
- **`FUTURE.md`** — Declared futures (present-tense statements about what's true when the project succeeds)
|
|
178
|
+
- **`MILESTONES.md`** — Milestones derived backward from futures
|
|
179
|
+
- **`.planning/milestones/M-XX-slug/PLAN.md`** — Actions for each milestone (title, produces, causes)
|
|
180
|
+
- **`.planning/STATE.md`** — Current project state and decisions
|
|
181
|
+
- **`.planning/PROJECT.md`** — Project context and background
|
|
182
|
+
|
|
183
|
+
### The DAG structure
|
|
184
|
+
|
|
185
|
+
Declarations (D-XX) → Milestones (M-XX) → Actions (A-XX). Each layer links to the one above via `realizes` (milestones → declarations) and `causes` (actions → milestones). Read the graph with:
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
node_modules/.bin/declare-cc load-graph
|
|
189
|
+
```
|
|
190
|
+
|
|
191
|
+
Returns JSON with `declarations`, `milestones`, `actions` arrays.
|
|
192
|
+
|
|
193
|
+
### Slash commands available to you
|
|
194
|
+
|
|
195
|
+
If you're running inside Claude Code with declare-cc installed, these slash commands are available:
|
|
196
|
+
|
|
197
|
+
- `/declare:status` — See where the project stands
|
|
198
|
+
- `/declare:execute M-XX` — Execute actions for a milestone
|
|
199
|
+
- `/declare:verify M-XX` — Validate deliverables
|
|
200
|
+
- `/declare:trace A-XX` — Understand why an action exists (walk the why-chain)
|
|
201
|
+
- `/declare:progress` — Find the next thing to work on
|
|
202
|
+
- `/declare:help` — See all commands
|
|
203
|
+
|
|
204
|
+
### Dashboard API
|
|
205
|
+
|
|
206
|
+
If the server is running (check `.planning/server.port`), you can use the HTTP API:
|
|
129
207
|
|
|
130
|
-
|
|
208
|
+
| Method | Path | Returns |
|
|
209
|
+
|--------|------|---------|
|
|
210
|
+
| GET | `/api/graph` | Full DAG (declarations, milestones, actions) |
|
|
211
|
+
| GET | `/api/status` | Integrity/alignment metrics |
|
|
212
|
+
| GET | `/api/agents` | Running/completed agents |
|
|
213
|
+
| GET | `/api/events` | SSE stream (real-time updates) |
|
|
214
|
+
| POST | `/api/review` | Approve/reject a node |
|
|
215
|
+
| POST | `/api/action/:id/execute` | Execute an action |
|
|
131
216
|
|
|
132
217
|
---
|
|
133
218
|
|
|
@@ -145,7 +230,7 @@ The dashboard is the primary interface. All operations are also available as sla
|
|
|
145
230
|
| `/declare:audit M-XX` | Cross-reference against declarations |
|
|
146
231
|
| `/declare:trace A-XX` | Walk the why-chain to its declaration |
|
|
147
232
|
| `/declare:status` | Graph health and layer counts |
|
|
148
|
-
| `/declare:dashboard` |
|
|
233
|
+
| `/declare:dashboard` | Start server and print URL |
|
|
149
234
|
| `/declare:help` | Show all commands |
|
|
150
235
|
|
|
151
236
|
---
|
package/dist/declare-tools.cjs
CHANGED
|
@@ -1552,7 +1552,7 @@ var require_help = __commonJS({
|
|
|
1552
1552
|
usage: "/declare:help"
|
|
1553
1553
|
}
|
|
1554
1554
|
],
|
|
1555
|
-
version: "1.0.
|
|
1555
|
+
version: "1.0.7"
|
|
1556
1556
|
};
|
|
1557
1557
|
}
|
|
1558
1558
|
module2.exports = { runHelp: runHelp2 };
|
|
@@ -4302,7 +4302,7 @@ var require_ai_runner = __commonJS({
|
|
|
4302
4302
|
const env = { ...process.env };
|
|
4303
4303
|
delete env.CLAUDECODE;
|
|
4304
4304
|
const queryOpts = {
|
|
4305
|
-
model: opts.model || "
|
|
4305
|
+
model: opts.model || "sonnet",
|
|
4306
4306
|
maxTurns: opts.maxTurns || 1,
|
|
4307
4307
|
cwd: opts.cwd || process.cwd(),
|
|
4308
4308
|
abortController,
|
|
@@ -4712,7 +4712,7 @@ data: ${JSON.stringify(data)}
|
|
|
4712
4712
|
sessions.set(sessionId, { milestoneId: milestone.id, agentId, abortController, startTime: Date.now() });
|
|
4713
4713
|
runAI(prompt, {
|
|
4714
4714
|
cwd,
|
|
4715
|
-
model: "
|
|
4715
|
+
model: "opus",
|
|
4716
4716
|
maxTurns: 1,
|
|
4717
4717
|
abortController,
|
|
4718
4718
|
onText: (text) => {
|
|
@@ -7396,7 +7396,7 @@ If the current version is already good, output exactly: LGTM \u2014 no changes n
|
|
|
7396
7396
|
const isWriteMode = mode === "write";
|
|
7397
7397
|
runAI(prompt, {
|
|
7398
7398
|
cwd,
|
|
7399
|
-
model: "
|
|
7399
|
+
model: "opus",
|
|
7400
7400
|
maxTurns: isWriteMode ? 10 : 1,
|
|
7401
7401
|
withTools: isWriteMode,
|
|
7402
7402
|
abortController,
|
|
@@ -7608,7 +7608,7 @@ The options array is optional \u2014 include it only when there are clear altern
|
|
|
7608
7608
|
discussSession = { sessionId, nodeId: id, abortController, agentId };
|
|
7609
7609
|
runAI(prompt, {
|
|
7610
7610
|
cwd,
|
|
7611
|
-
model: "
|
|
7611
|
+
model: "opus",
|
|
7612
7612
|
maxTurns: 1,
|
|
7613
7613
|
abortController,
|
|
7614
7614
|
onText: (text) => {
|
|
@@ -7944,7 +7944,7 @@ The options array is optional \u2014 include it only when there are clear altern
|
|
|
7944
7944
|
persistOnboardSession(cwd);
|
|
7945
7945
|
runAI(prompt, {
|
|
7946
7946
|
cwd,
|
|
7947
|
-
model: "
|
|
7947
|
+
model: "opus",
|
|
7948
7948
|
maxTurns: 1,
|
|
7949
7949
|
abortController,
|
|
7950
7950
|
onText: (text) => {
|
|
@@ -8723,6 +8723,10 @@ data: ${JSON.stringify({ reason: "delete", nodeId: id })}
|
|
|
8723
8723
|
req.on("close", () => sseClients.delete(res));
|
|
8724
8724
|
return;
|
|
8725
8725
|
}
|
|
8726
|
+
if (urlPath === "/api/version") {
|
|
8727
|
+
sendJson(res, 200, { version: true ? "1.0.7" : "dev" });
|
|
8728
|
+
return;
|
|
8729
|
+
}
|
|
8726
8730
|
if (urlPath === "/api/graph") {
|
|
8727
8731
|
handleGraph(res, cwd);
|
|
8728
8732
|
return;
|
package/dist/public/app.js
CHANGED
|
@@ -8722,8 +8722,6 @@ function cancelOnboard() {
|
|
|
8722
8722
|
}
|
|
8723
8723
|
|
|
8724
8724
|
function startOnboardApproving(mode) {
|
|
8725
|
-
onboardPhase = 'approving';
|
|
8726
|
-
onboardApproveIndex = 0;
|
|
8727
8725
|
if (mode === 'all') {
|
|
8728
8726
|
approveAllOnboard();
|
|
8729
8727
|
} else {
|
|
@@ -8731,6 +8729,62 @@ function startOnboardApproving(mode) {
|
|
|
8731
8729
|
}
|
|
8732
8730
|
}
|
|
8733
8731
|
|
|
8732
|
+
async function approveOnboardByIndex(idx) {
|
|
8733
|
+
if (!onboardProposals || idx >= onboardProposals.length) return;
|
|
8734
|
+
const proposal = onboardProposals[idx];
|
|
8735
|
+
if (proposal.approvedId) return; // already approved
|
|
8736
|
+
try {
|
|
8737
|
+
const resp = await fetch('/api/onboard/approve', {
|
|
8738
|
+
method: 'POST',
|
|
8739
|
+
headers: { 'Content-Type': 'application/json' },
|
|
8740
|
+
body: JSON.stringify({ title: proposal.title, statement: proposal.statement }),
|
|
8741
|
+
});
|
|
8742
|
+
const data = await resp.json();
|
|
8743
|
+
if (data.id) proposal.approvedId = data.id;
|
|
8744
|
+
} catch (_) {}
|
|
8745
|
+
|
|
8746
|
+
// Check if all done
|
|
8747
|
+
const remaining = onboardProposals.filter(p => !p.approvedId).length;
|
|
8748
|
+
if (remaining === 0) {
|
|
8749
|
+
onboardPhase = 'idle';
|
|
8750
|
+
onboardPrompt = null;
|
|
8751
|
+
onboardQuestions = null;
|
|
8752
|
+
onboardProposals = null;
|
|
8753
|
+
fetch('/api/onboard/complete', { method: 'POST' }).catch(() => {});
|
|
8754
|
+
loadData().then(() => renderDrillView());
|
|
8755
|
+
loadActivity();
|
|
8756
|
+
} else {
|
|
8757
|
+
renderOnboardUI();
|
|
8758
|
+
}
|
|
8759
|
+
}
|
|
8760
|
+
|
|
8761
|
+
function editOnboardByIndex(idx) {
|
|
8762
|
+
if (!onboardProposals || idx >= onboardProposals.length) return;
|
|
8763
|
+
const proposal = onboardProposals[idx];
|
|
8764
|
+
const card = document.querySelector(`[data-onboard-idx="${idx}"]`);
|
|
8765
|
+
if (!card) return;
|
|
8766
|
+
const body = card.querySelector('.drill-card-body');
|
|
8767
|
+
if (!body) return;
|
|
8768
|
+
body.innerHTML = `
|
|
8769
|
+
<input class="onboard-title" value="${escHtml(proposal.title)}" style="width:100%;margin-bottom:4px" />
|
|
8770
|
+
<textarea class="onboard-statement" rows="2" style="width:100%">${escHtml(proposal.statement)}</textarea>
|
|
8771
|
+
<div style="margin-top:6px">
|
|
8772
|
+
<button class="drill-review-btn approve-btn onboard-save-btn" data-onboard-idx="${idx}">Save</button>
|
|
8773
|
+
<button class="drill-action-btn onboard-cancel-edit-btn" data-onboard-idx="${idx}">Cancel</button>
|
|
8774
|
+
</div>
|
|
8775
|
+
`;
|
|
8776
|
+
const titleInput = body.querySelector('.onboard-title');
|
|
8777
|
+
if (titleInput) titleInput.focus();
|
|
8778
|
+
body.querySelector('.onboard-save-btn').addEventListener('click', () => {
|
|
8779
|
+
proposal.title = body.querySelector('.onboard-title').value.trim() || proposal.title;
|
|
8780
|
+
proposal.statement = body.querySelector('.onboard-statement').value.trim() || proposal.statement;
|
|
8781
|
+
renderOnboardUI();
|
|
8782
|
+
});
|
|
8783
|
+
body.querySelector('.onboard-cancel-edit-btn').addEventListener('click', () => {
|
|
8784
|
+
renderOnboardUI();
|
|
8785
|
+
});
|
|
8786
|
+
}
|
|
8787
|
+
|
|
8734
8788
|
async function approveCurrentOnboard() {
|
|
8735
8789
|
if (!onboardProposals || onboardApproveIndex >= onboardProposals.length) return;
|
|
8736
8790
|
|
|
@@ -8853,53 +8907,62 @@ function renderOnboardUI() {
|
|
|
8853
8907
|
`;
|
|
8854
8908
|
} else {
|
|
8855
8909
|
let html = '<div class="onboard-phase-label">Proposed Declarations</div>';
|
|
8910
|
+
html += '<div class="drill-cards onboard-cards">';
|
|
8856
8911
|
onboardProposals.forEach((p, i) => {
|
|
8857
|
-
|
|
8858
|
-
|
|
8859
|
-
|
|
8912
|
+
const isApproved = !!p.approvedId;
|
|
8913
|
+
html += `<div class="drill-card${isApproved ? '' : ' needs-review'}${!isApproved && i === 0 ? ' current-review' : ''}" data-onboard-idx="${i}">
|
|
8914
|
+
<div class="drill-card-top">
|
|
8915
|
+
<span class="drill-card-id">${isApproved ? escHtml(p.approvedId) : (i + 1) + '.'}</span>
|
|
8916
|
+
<div class="drill-card-body">
|
|
8917
|
+
<div class="drill-card-title">${escHtml(p.title)}</div>
|
|
8918
|
+
<div class="drill-card-desc">${escHtml(p.statement)}</div>
|
|
8919
|
+
${p.reasoning ? `<div class="drill-card-desc" style="opacity:0.5;font-style:italic;margin-top:4px">${escHtml(p.reasoning)}</div>` : ''}
|
|
8920
|
+
</div>
|
|
8921
|
+
</div>
|
|
8922
|
+
<div class="drill-card-actions">
|
|
8923
|
+
${isApproved
|
|
8924
|
+
? '<span style="color:var(--approved-color);font-weight:600;font-size:12px">Approved</span>'
|
|
8925
|
+
: `<button class="drill-review-btn approve-btn" data-onboard-action="approve" data-onboard-idx="${i}"><kbd>A</kbd> Approve</button>
|
|
8926
|
+
<button class="drill-action-btn" data-onboard-action="edit" data-onboard-idx="${i}"><kbd>E</kbd> Edit</button>
|
|
8927
|
+
<button class="drill-action-btn drill-action-danger" data-onboard-action="delete" data-onboard-idx="${i}"><kbd>D</kbd> Delete</button>`
|
|
8928
|
+
}
|
|
8860
8929
|
</div>
|
|
8861
|
-
<input class="onboard-title" value="${escHtml(p.title)}" />
|
|
8862
|
-
<textarea class="onboard-statement" rows="2">${escHtml(p.statement)}</textarea>
|
|
8863
|
-
<div class="onboard-reason">${escHtml(p.reasoning || '')}</div>
|
|
8864
8930
|
</div>`;
|
|
8865
8931
|
});
|
|
8866
|
-
html +=
|
|
8867
|
-
|
|
8868
|
-
|
|
8869
|
-
|
|
8870
|
-
|
|
8932
|
+
html += '</div>';
|
|
8933
|
+
const unapprovedCount = onboardProposals.filter(p => !p.approvedId).length;
|
|
8934
|
+
if (unapprovedCount > 0) {
|
|
8935
|
+
html += `<div class="onboard-actions">
|
|
8936
|
+
<button class="onboard-btn-primary" onclick="startOnboardApproving('all')"><kbd>⌃⇧A</kbd> Approve All (${unapprovedCount})</button>
|
|
8937
|
+
<button class="onboard-btn-secondary" onclick="cancelOnboard()">Cancel</button>
|
|
8938
|
+
</div>`;
|
|
8939
|
+
}
|
|
8871
8940
|
container.innerHTML = html;
|
|
8941
|
+
|
|
8942
|
+
// Wire card buttons
|
|
8943
|
+
setTimeout(() => {
|
|
8944
|
+
container.querySelectorAll('[data-onboard-action]').forEach(btn => {
|
|
8945
|
+
btn.addEventListener('click', (e) => {
|
|
8946
|
+
e.stopPropagation();
|
|
8947
|
+
const idx = parseInt(btn.dataset.onboardIdx);
|
|
8948
|
+
const action = btn.dataset.onboardAction;
|
|
8949
|
+
if (action === 'approve') {
|
|
8950
|
+
approveOnboardByIndex(idx);
|
|
8951
|
+
} else if (action === 'edit') {
|
|
8952
|
+
editOnboardByIndex(idx);
|
|
8953
|
+
} else if (action === 'delete') {
|
|
8954
|
+
onboardProposals.splice(idx, 1);
|
|
8955
|
+
renderOnboardUI();
|
|
8956
|
+
}
|
|
8957
|
+
});
|
|
8958
|
+
});
|
|
8959
|
+
}, 0);
|
|
8872
8960
|
}
|
|
8873
8961
|
} else if (onboardPhase === 'approving') {
|
|
8874
|
-
|
|
8875
|
-
|
|
8876
|
-
|
|
8877
|
-
|
|
8878
|
-
const isApproved = i < onboardApproveIndex || p.approvedId;
|
|
8879
|
-
const isCurrent = i === onboardApproveIndex && !p.approvedId;
|
|
8880
|
-
const cls = isApproved ? 'approved' : isCurrent ? 'current' : '';
|
|
8881
|
-
html += `<div class="onboard-proposal ${cls}">
|
|
8882
|
-
<div class="op-header">
|
|
8883
|
-
${isApproved ? '<span class="op-check">\u2713</span>' : `<span class="op-index">${i + 1}.</span>`}
|
|
8884
|
-
${p.approvedId ? `<span class="op-id">${escHtml(p.approvedId)}</span>` : ''}
|
|
8885
|
-
</div>
|
|
8886
|
-
${isCurrent ? `
|
|
8887
|
-
<input class="onboard-title" value="${escHtml(p.title)}" />
|
|
8888
|
-
<textarea class="onboard-statement" rows="2">${escHtml(p.statement)}</textarea>
|
|
8889
|
-
` : `
|
|
8890
|
-
<div style="font-weight:600;font-size:13px">${escHtml(p.title)}</div>
|
|
8891
|
-
<div style="font-size:12px;color:var(--text-dim);margin-top:4px">${escHtml(p.statement)}</div>
|
|
8892
|
-
`}
|
|
8893
|
-
<div class="onboard-reason">${escHtml(p.reasoning || '')}</div>
|
|
8894
|
-
</div>`;
|
|
8895
|
-
});
|
|
8896
|
-
if (onboardApproveIndex < onboardProposals.length) {
|
|
8897
|
-
html += `<div class="onboard-actions">
|
|
8898
|
-
<button class="onboard-btn-primary" onclick="approveCurrentOnboard()">Approve & Next</button>
|
|
8899
|
-
<button class="onboard-btn-secondary" onclick="cancelOnboard()">Cancel</button>
|
|
8900
|
-
</div>`;
|
|
8901
|
-
}
|
|
8902
|
-
container.innerHTML = html;
|
|
8962
|
+
// Redirect to proposals view — approving is now inline
|
|
8963
|
+
onboardPhase = 'proposals';
|
|
8964
|
+
renderOnboardUI();
|
|
8965
|
+
return;
|
|
8903
8966
|
}
|
|
8904
8967
|
|
|
8905
8968
|
$drillList.appendChild(container);
|
|
@@ -9013,5 +9076,11 @@ loadData().then(() => {
|
|
|
9013
9076
|
loadActivity();
|
|
9014
9077
|
loadAgentCards();
|
|
9015
9078
|
|
|
9079
|
+
// Version label
|
|
9080
|
+
fetch('/api/version').then(r => r.json()).then(d => {
|
|
9081
|
+
const el = document.getElementById('version-label');
|
|
9082
|
+
if (el && d.version) el.textContent = 'v' + d.version;
|
|
9083
|
+
}).catch(() => {});
|
|
9084
|
+
|
|
9016
9085
|
// Poll agent cards every 3s as fallback if SSE agent events are missed
|
|
9017
9086
|
setInterval(loadAgentCards, 3000);
|
package/dist/public/index.html
CHANGED
|
@@ -1187,6 +1187,15 @@
|
|
|
1187
1187
|
@keyframes derive-spin {
|
|
1188
1188
|
to { transform: rotate(360deg); }
|
|
1189
1189
|
}
|
|
1190
|
+
#version-label {
|
|
1191
|
+
position: fixed;
|
|
1192
|
+
bottom: 6px;
|
|
1193
|
+
left: 8px;
|
|
1194
|
+
font-size: 10px;
|
|
1195
|
+
color: #555;
|
|
1196
|
+
pointer-events: none;
|
|
1197
|
+
z-index: 1;
|
|
1198
|
+
}
|
|
1190
1199
|
.derive-card-status {
|
|
1191
1200
|
display: inline-flex;
|
|
1192
1201
|
align-items: center;
|
|
@@ -4277,6 +4286,7 @@
|
|
|
4277
4286
|
</div>
|
|
4278
4287
|
</div>
|
|
4279
4288
|
|
|
4280
|
-
<
|
|
4289
|
+
<div id="version-label"></div>
|
|
4290
|
+
<script src="/public/app.js?v=27"></script>
|
|
4281
4291
|
</body>
|
|
4282
4292
|
</html>
|
package/package.json
CHANGED