@skilly-hand/skilly-hand 0.16.1 → 0.17.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 +18 -0
- package/catalog/skills/spec-driven-development/SKILL.md +1 -1
- package/catalog/skills/spec-driven-development/agents/orchestrate.md +1 -1
- package/catalog/skills/spec-driven-development/agents/verify.md +9 -1
- package/catalog/skills/spec-driven-development/assets/validation-checklist.md +1 -0
- package/package.json +2 -2
- package/packages/catalog/package.json +1 -1
- package/packages/cli/package.json +1 -1
- package/packages/core/package.json +1 -1
- package/packages/core/src/index.js +82 -5
- package/packages/detectors/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -16,6 +16,24 @@ All notable changes to this project are documented in this file.
|
|
|
16
16
|
### Removed
|
|
17
17
|
- _None._
|
|
18
18
|
|
|
19
|
+
## [0.17.0] - 2026-04-08
|
|
20
|
+
[View on npm](https://www.npmjs.com/package/@skilly-hand/skilly-hand/v/0.17.0)
|
|
21
|
+
|
|
22
|
+
### Added
|
|
23
|
+
- Added sandbox harness and matrix integration tests (`scripts/test-in-sandbox.mjs`, `tests/sandbox-harness.test.js`, `tests/sandbox-matrix.test.js`) and wired them into the root test pipeline.
|
|
24
|
+
- Added required `review-rangers` final-gate guidance to the spec-driven-development verify flow and validation checklist.
|
|
25
|
+
|
|
26
|
+
### Changed
|
|
27
|
+
- Updated installer reconciliation logic to remove stale managed targets when agent selection narrows, while preserving/restoring backups for retained targets.
|
|
28
|
+
- Updated backup behavior to skip backup creation for files already marked as managed content.
|
|
29
|
+
- Updated root `npm test` to run sandbox integration verification after the node test suite.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
- Fixed uninstall and re-install behavior for narrowed agent selections by restoring original files and cleaning obsolete managed artifacts.
|
|
33
|
+
|
|
34
|
+
### Removed
|
|
35
|
+
- _None._
|
|
36
|
+
|
|
19
37
|
## [0.16.1] - 2026-04-08
|
|
20
38
|
[View on npm](https://www.npmjs.com/package/@skilly-hand/skilly-hand/v/0.16.1)
|
|
21
39
|
|
|
@@ -22,7 +22,7 @@ Do not use this skill for:
|
|
|
22
22
|
1. Define the spec in `.sdd/active/<feature-name>/spec.md`.
|
|
23
23
|
2. Review and refine scope, constraints, and tasks.
|
|
24
24
|
3. Execute one small task at a time.
|
|
25
|
-
4. Verify each task and the end-to-end outcome.
|
|
25
|
+
4. Verify each task and the end-to-end outcome, ending with a required `review-rangers` final gate.
|
|
26
26
|
5. Archive to `.sdd/archive/` when complete.
|
|
27
27
|
|
|
28
28
|
Recommended task size:
|
|
@@ -14,7 +14,7 @@ Coordinate planning, implementation, and verification through explicit checkpoin
|
|
|
14
14
|
1. PLAN: Produce or update the spec.
|
|
15
15
|
2. REVIEW CHECKPOINT: Confirm the plan is approved.
|
|
16
16
|
3. APPLY: Execute agreed task batch.
|
|
17
|
-
4. VERIFY CHECKPOINT: Validate outputs against the spec.
|
|
17
|
+
4. VERIFY CHECKPOINT: Validate outputs against the spec and run the required final `review-rangers` gate.
|
|
18
18
|
5. REPEAT: Continue by phase or task batch.
|
|
19
19
|
6. ARCHIVE: Move completed work from `.sdd/active/` to `.sdd/archive/`.
|
|
20
20
|
|
|
@@ -15,7 +15,15 @@ Validate that implementation matches the approved spec and passes quality checks
|
|
|
15
15
|
2. Run task-level verification evidence checks.
|
|
16
16
|
3. Run feature-level validation commands.
|
|
17
17
|
4. Confirm constraints (`MUST`, `MUST NOT`) were respected.
|
|
18
|
-
5.
|
|
18
|
+
5. Run a final structured `review-rangers` pass over the full change set.
|
|
19
|
+
6. Report pass/fail per area with concrete evidence.
|
|
20
|
+
|
|
21
|
+
### Required Final Gate (`review-rangers`)
|
|
22
|
+
|
|
23
|
+
- Validate selected agent targets vs actual instruction files/symlinks written.
|
|
24
|
+
- Validate stale managed target cleanup after re-install/reselection.
|
|
25
|
+
- Validate backup and restore safety (including uninstall restore behavior).
|
|
26
|
+
- Any unresolved `review-rangers` blocker keeps verification in failed state.
|
|
19
27
|
|
|
20
28
|
## Quality Bar
|
|
21
29
|
|
|
@@ -28,5 +28,6 @@ Use this checklist before implementation and again before archive.
|
|
|
28
28
|
- [ ] All planned tasks are complete.
|
|
29
29
|
- [ ] Feature-level validation passes.
|
|
30
30
|
- [ ] Constraints were respected.
|
|
31
|
+
- [ ] Final `review-rangers` gate completed with no unresolved blockers.
|
|
31
32
|
- [ ] No unintended scope creep.
|
|
32
33
|
- [ ] Work is moved from `.sdd/active/` to `.sdd/archive/`.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@skilly-hand/skilly-hand",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.17.0",
|
|
4
4
|
"license": "CC-BY-NC-4.0",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"publishConfig": {
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
"catalog:check": "node ./scripts/check-catalog.mjs",
|
|
29
29
|
"catalog:sync": "node ./scripts/sync-catalog-readme.mjs",
|
|
30
30
|
"agentic:self:sync": "node ./scripts/sync-self-agentic.mjs",
|
|
31
|
-
"test": "node --test tests/*.test.js",
|
|
31
|
+
"test": "node --test tests/*.test.js && node ./scripts/test-in-sandbox.mjs",
|
|
32
32
|
"security:check": "node ./scripts/security-check.mjs",
|
|
33
33
|
"verify:packlist": "node ./scripts/verify-packlist.mjs",
|
|
34
34
|
"verify:versions": "node ./scripts/verify-versions.mjs",
|
|
@@ -175,7 +175,11 @@ async function ensureManagedTextFile(targetPath, content, backupsDir, lockData)
|
|
|
175
175
|
return;
|
|
176
176
|
}
|
|
177
177
|
|
|
178
|
-
|
|
178
|
+
// Do not back up previously managed content; backups are for restoring
|
|
179
|
+
// user-authored files replaced by managed files.
|
|
180
|
+
if (!current.includes(MANAGED_MARKER)) {
|
|
181
|
+
await backupPathIfNeeded(targetPath, backupsDir, lockData);
|
|
182
|
+
}
|
|
179
183
|
}
|
|
180
184
|
|
|
181
185
|
await mkdir(path.dirname(targetPath), { recursive: true });
|
|
@@ -230,6 +234,69 @@ function buildInstallTargets(selectedAgents) {
|
|
|
230
234
|
};
|
|
231
235
|
}
|
|
232
236
|
|
|
237
|
+
async function reconcileManagedTargets({
|
|
238
|
+
previousLock,
|
|
239
|
+
selectedInstructionTargets,
|
|
240
|
+
selectedSkillTargets,
|
|
241
|
+
lockData
|
|
242
|
+
}) {
|
|
243
|
+
if (!previousLock) {
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const selectedInstructions = new Set(selectedInstructionTargets);
|
|
248
|
+
const selectedSkills = new Set(selectedSkillTargets);
|
|
249
|
+
const selectedTargets = new Set([...selectedInstructions, ...selectedSkills]);
|
|
250
|
+
const previousBackups = previousLock.backups || {};
|
|
251
|
+
|
|
252
|
+
for (const [targetPath, backupPath] of Object.entries(previousBackups)) {
|
|
253
|
+
if (!selectedTargets.has(targetPath)) {
|
|
254
|
+
continue;
|
|
255
|
+
}
|
|
256
|
+
if (await exists(backupPath)) {
|
|
257
|
+
lockData.backups[targetPath] = backupPath;
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
for (const symlinkPath of previousLock.managedSymlinks || []) {
|
|
262
|
+
if (selectedSkills.has(symlinkPath)) {
|
|
263
|
+
continue;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
if (await exists(symlinkPath)) {
|
|
267
|
+
await rm(symlinkPath, { recursive: true, force: true });
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
const backupPath = previousBackups[symlinkPath];
|
|
271
|
+
if (backupPath && await exists(backupPath)) {
|
|
272
|
+
await mkdir(path.dirname(symlinkPath), { recursive: true });
|
|
273
|
+
await cp(backupPath, symlinkPath, { recursive: true, force: true });
|
|
274
|
+
}
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
for (const filePath of previousLock.managedFiles || []) {
|
|
278
|
+
if (selectedInstructions.has(filePath)) {
|
|
279
|
+
continue;
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
const backupPath = previousBackups[filePath];
|
|
283
|
+
if (backupPath && await exists(backupPath)) {
|
|
284
|
+
await mkdir(path.dirname(filePath), { recursive: true });
|
|
285
|
+
await cp(backupPath, filePath, { recursive: true, force: true });
|
|
286
|
+
continue;
|
|
287
|
+
}
|
|
288
|
+
|
|
289
|
+
if (!(await exists(filePath))) {
|
|
290
|
+
continue;
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
const content = await readFile(filePath, "utf8");
|
|
294
|
+
if (content.includes(MANAGED_MARKER)) {
|
|
295
|
+
await rm(filePath, { force: true });
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
|
|
233
300
|
export async function installProject({
|
|
234
301
|
cwd,
|
|
235
302
|
agents,
|
|
@@ -259,6 +326,7 @@ export async function installProject({
|
|
|
259
326
|
const targetCatalogDir = path.join(installRoot, "catalog");
|
|
260
327
|
const backupsDir = path.join(installRoot, "backups");
|
|
261
328
|
const lockPath = path.join(installRoot, "manifest.lock.json");
|
|
329
|
+
const previousLock = await exists(lockPath) ? await readJson(lockPath) : null;
|
|
262
330
|
const lockData = {
|
|
263
331
|
version: 1,
|
|
264
332
|
generatedAt: plan.generatedAt,
|
|
@@ -289,15 +357,24 @@ export async function installProject({
|
|
|
289
357
|
await writeFile(path.join(installRoot, "AGENTS.md"), agentsMarkdown, "utf8");
|
|
290
358
|
|
|
291
359
|
const { instructionTargets, skillTargets } = buildInstallTargets(selectedAgents);
|
|
360
|
+
const absoluteInstructionTargets = instructionTargets.map((pathParts) => path.join(cwd, ...pathParts));
|
|
361
|
+
const absoluteSkillTargets = skillTargets.map((pathParts) => path.join(cwd, ...pathParts));
|
|
362
|
+
|
|
363
|
+
await reconcileManagedTargets({
|
|
364
|
+
previousLock,
|
|
365
|
+
selectedInstructionTargets: absoluteInstructionTargets,
|
|
366
|
+
selectedSkillTargets: absoluteSkillTargets,
|
|
367
|
+
lockData
|
|
368
|
+
});
|
|
292
369
|
|
|
293
|
-
for (const
|
|
294
|
-
await ensureManagedTextFile(
|
|
370
|
+
for (const targetPath of absoluteInstructionTargets) {
|
|
371
|
+
await ensureManagedTextFile(targetPath, agentsMarkdown, backupsDir, lockData);
|
|
295
372
|
}
|
|
296
373
|
|
|
297
374
|
const skillsSourcePath = path.join(installRoot, "catalog");
|
|
298
375
|
|
|
299
|
-
for (const
|
|
300
|
-
await ensureSymlink(
|
|
376
|
+
for (const targetPath of absoluteSkillTargets) {
|
|
377
|
+
await ensureSymlink(targetPath, skillsSourcePath, backupsDir, lockData);
|
|
301
378
|
}
|
|
302
379
|
|
|
303
380
|
await writeJson(lockPath, lockData);
|