@1aboveio/skills 0.20.2 → 0.20.4
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 +14 -6
- package/package.json +1 -1
- package/runtime/skills/distribution/generated/recipes.json +220 -25
- package/runtime/skills/distribution/scripts/bundles.mjs +167 -48
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -0
- package/runtime/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +1 -1
- package/skills/compliance/compliance-audit/LICENSE +3 -0
- package/skills/compliance/compliance-audit/SKILL.md +466 -0
- package/skills/compliance/compliance-audit/references/findings-schema.md +290 -0
- package/skills/compliance/compliance-audit/references/vars-checklist.md +1068 -0
- package/skills/compliance/compliance-audit/references/vars-findings-schema.md +395 -0
- package/skills/compliance/compliance-audit/scripts/reconcile_tracker.py +454 -0
- package/skills/compliance/compliance-audit/scripts/test_validate_tracker.py +90 -0
- package/skills/compliance/compliance-audit/scripts/validate_tracker.py +752 -0
- package/skills/compliance/doc-authenticity/LICENSE +3 -0
- package/skills/compliance/doc-authenticity/SKILL.md +158 -0
- package/skills/compliance/doc-authenticity/references/agreement.md +66 -0
- package/skills/compliance/doc-authenticity/references/bank-statement.md +150 -0
- package/skills/compliance/doc-authenticity/references/forensic-signals.md +157 -0
- package/skills/compliance/doc-authenticity/references/image-signals.md +114 -0
- package/skills/compliance/doc-authenticity/references/invoice.md +66 -0
- package/skills/compliance/doc-authenticity/references/issuer-profiles.md +86 -0
- package/skills/compliance/doc-authenticity/references/verdict-and-report.md +95 -0
- package/skills/compliance/doc-authenticity/references/verdict.schema.json +81 -0
- package/skills/compliance/doc-authenticity/scripts/image_forensics.py +294 -0
- package/skills/compliance/doc-authenticity/scripts/pdf_forensics.py +401 -0
- package/skills/compliance/doc-authenticity/scripts/validate_codes.py +224 -0
- package/skills/compliance/doc-authenticity/scripts/validate_verdict.py +308 -0
- package/skills/compliance/report-writing/LICENSE +3 -0
- package/skills/compliance/report-writing/SKILL.md +206 -0
- package/skills/compliance/report-writing/references/audit-report-template.md +121 -0
- package/skills/compliance/report-writing/references/external-assessment-template.md +167 -0
- package/skills/compliance/report-writing/references/management-report-template.md +145 -0
- package/skills/compliance/report-writing/references/vars-report-template.md +224 -0
- package/skills/document-utils/doc-to-md/LICENSE +3 -0
- package/skills/document-utils/doc-to-md/SKILL.md +77 -0
- package/skills/document-utils/doc-to-md/scripts/convert.py +157 -0
- package/skills/document-utils/reg-parser/LICENSE +3 -0
- package/skills/document-utils/reg-parser/SKILL.md +201 -0
- package/skills/document-utils/reg-parser/references/checklist-schema.md +223 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-amlo-checklist.json +1306 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-amlo-checklist.md +469 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.json +634 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-fi-corporate-onboarding-cdd-checklist.md +233 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-mso-guideline-checklist.json +2194 -0
- package/skills/document-utils/reg-parser/references/checklists/hk-mso-guideline-checklist.md +771 -0
- package/skills/document-utils/reg-parser/references/checklists/kyc-aml-checklist.md +258 -0
- package/skills/document-utils/reg-parser/references/checklists/visa-vars-checklist.md +1058 -0
- package/skills/document-utils/reg-parser/references/mandates/hk-amlo-cap615.md +389 -0
- package/skills/document-utils/reg-parser/references/mandates/visa-vars.md +205 -0
- package/skills/document-utils/reg-parser/scripts/compare_checklist.py +228 -0
- package/skills/document-utils/reg-parser/scripts/dump_checklist.py +348 -0
- package/skills/document-utils/reg-parser/scripts/preprocess-mandate.sh +171 -0
- package/skills/document-utils/reg-parser/scripts/test_quarantine.sh +102 -0
- package/skills/document-utils/reg-parser/scripts/validate_checklist.py +532 -0
- package/skills/document-utils/reg-parser/scripts/validate_mandate.py +242 -0
- package/skills/engineering/engineering-runtime/coherence/workflow.json +16 -16
- package/skills/engineering/engineering-runtime/scripts/workflow-coherence.mjs +4 -0
- package/skills/engineering/engineering-runtime/scripts/workflow-policy.mjs +1 -1
- package/skills/payment/similar-domain/LICENSE +3 -0
- package/skills/payment/similar-domain/SKILL.md +178 -0
- package/skills/payment/similar-domain/references/entities.md +58 -0
- package/skills/payment/similar-domain/references/fingerprints.md +82 -0
- package/skills/payment/similar-domain/references/pivots.md +66 -0
- package/skills/payment/similar-domain/references/report-template.md +76 -0
- package/skills/payment/similar-domain/references/scoring.md +61 -0
- package/skills/payment/similar-domain/references/terminology.md +37 -0
- package/skills/payment/similar-domain/references/workflow.md +79 -0
- package/skills/payment/tld-detection/SKILL.md +76 -21
- package/skills/payment/tld-detection/references/clean.md +1 -1
- package/skills/payment/tld-detection/references/clustering.md +21 -6
- package/skills/payment/tld-detection/references/decision.md +7 -2
- package/skills/payment/tld-detection/references/fx-conversion.md +229 -0
- package/skills/payment/tld-detection/references/report-template.md +63 -27
- package/skills/payment/tld-detection/references/streams.md +90 -0
- package/skills/payment/tld-detection/references/terminology.md +15 -7
- package/skills/payment/tld-detection/references/workflow.md +1 -1
- package/skills/payment/tld-detection/scripts/detect_streams.py +542 -0
- package/skills/engineering/ensure-coverage/evals/evals.json +0 -125
- package/skills/engineering/rush-issues/evals/evals.json +0 -65
- package/skills/engineering/rush-release/evals/evals.json +0 -58
- package/skills/fullstack/shadcn/evals/evals.json +0 -90
- package/skills/payment/fraud-analysis/evals/evals.json +0 -40
- package/skills/payment/tld-detection/evals/evals.json +0 -107
- package/skills/payment/tld-detection/tests/test_detect_ladders.py +0 -132
|
@@ -305,19 +305,14 @@ function localFirstPartyCommand(command, localRoot, groupId) {
|
|
|
305
305
|
return command.replace(NATIVE_ADD_SOURCE, (_match, prefix) => `${prefix}${shellArgument(localRoot)}`);
|
|
306
306
|
}
|
|
307
307
|
|
|
308
|
-
// The coordinator
|
|
309
|
-
//
|
|
310
|
-
//
|
|
311
|
-
//
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
//
|
|
315
|
-
//
|
|
316
|
-
const SKILL_TAIL = /^(.*? --skill )(.+?)( && .*)?$/;
|
|
317
|
-
|
|
318
|
-
// The generated step binds its runtime suffix (` && npm ci ...`) to the whole coalesced command.
|
|
319
|
-
// Per skill it belongs to the runtime member's own add, so the dependency install runs only after
|
|
320
|
-
// engineering-runtime itself was actually added — never after a sibling, never when it was skipped.
|
|
308
|
+
// The coordinator preserves generated dependency/member order, then coalesces each contiguous run
|
|
309
|
+
// from one source into a native add. A member shared by groups is included once and retains every
|
|
310
|
+
// owning selection for memory. Per-member attribution comes from post-install digest verification,
|
|
311
|
+
// not process count.
|
|
312
|
+
const SKILL_TAIL = /^(.*? --skill )(.+?)( --yes)?( && .*)?$/;
|
|
313
|
+
|
|
314
|
+
// The generated step binds its runtime suffix (` && npm ci ...`) to engineering-runtime. Keep it on
|
|
315
|
+
// the source batch only when that member remains selected after digest and consent filtering.
|
|
321
316
|
const RUNTIME_MEMBER = 'engineering-runtime';
|
|
322
317
|
|
|
323
318
|
function skillsFromCommand(command) {
|
|
@@ -329,14 +324,14 @@ function skillsFromCommand(command) {
|
|
|
329
324
|
function withSkills(command, skills) {
|
|
330
325
|
const match = SKILL_TAIL.exec(command);
|
|
331
326
|
if (!match) throw new Error(`Native add has no --skill list to rewrite: ${command}`);
|
|
332
|
-
return `${match[1]}${skills.join(' ')}${match[3] || ''}`;
|
|
327
|
+
return `${match[1]}${skills.join(' ')}${match[3] || ''}${match[4] || ''}`;
|
|
333
328
|
}
|
|
334
329
|
|
|
335
330
|
function withYesFlag(command) {
|
|
336
331
|
// --yes must bind to `skills`, not to a trailing `&& npm ci ...`.
|
|
337
332
|
const match = SKILL_TAIL.exec(command);
|
|
338
333
|
if (!match) return `${command} --yes`;
|
|
339
|
-
return `${match[1]}${match[2]} --yes${match[
|
|
334
|
+
return `${match[1]}${match[2]} --yes${match[4] || ''}`;
|
|
340
335
|
}
|
|
341
336
|
|
|
342
337
|
export function planBundleCommands(action, groupIds, {
|
|
@@ -354,18 +349,19 @@ export function planBundleCommands(action, groupIds, {
|
|
|
354
349
|
const recipes = readRecipes(recipesPath);
|
|
355
350
|
assertLocalRoot(localRoot);
|
|
356
351
|
const groups = new Map(recipes.groups.map((group) => [group.id, group]));
|
|
352
|
+
const memberMetadata = new Map((recipes.sources ?? [])
|
|
353
|
+
.flatMap((source) => source.members ?? [])
|
|
354
|
+
.map((member) => [member.installName, member]));
|
|
357
355
|
const requested = [];
|
|
358
356
|
for (const groupId of groupIds) {
|
|
359
357
|
if (!groups.has(groupId) || SUPPORT_GROUPS.has(groupId)) throw new Error(`Unknown bundle: ${groupId}`);
|
|
360
358
|
if (!requested.includes(groupId)) requested.push(groupId);
|
|
361
359
|
}
|
|
362
360
|
|
|
363
|
-
// Plan
|
|
364
|
-
//
|
|
365
|
-
// would install skills that are not in the selection / not in the package.
|
|
361
|
+
// Plan individual member records first. The execution boundary coalesces adjacent records from
|
|
362
|
+
// the same source without weakening per-member dependency, policy, or selection metadata.
|
|
366
363
|
const planned = [];
|
|
367
364
|
const plannedByMember = new Map();
|
|
368
|
-
|
|
369
365
|
for (const groupId of requested) {
|
|
370
366
|
const group = groups.get(groupId);
|
|
371
367
|
for (const step of group[action].steps) {
|
|
@@ -376,14 +372,10 @@ export function planBundleCommands(action, groupIds, {
|
|
|
376
372
|
const command = step.sourceId === 'first-party'
|
|
377
373
|
? localFirstPartyCommand(generated.command, localRoot, groupId)
|
|
378
374
|
: generated.command;
|
|
379
|
-
// An explicit member subset (the digest skip) removes members from the native add but never
|
|
380
|
-
// reorders the rest: the remaining skills keep recipe order, support dependency first.
|
|
381
|
-
// Declined new members (#1128) are also filtered out, but consent never applies to support
|
|
382
|
-
// dependencies (harness-runtime): they are silent install dependencies, not capabilities.
|
|
383
375
|
const skills = skillsFromCommand(command)
|
|
384
376
|
.filter((skill) => !skipMembers?.has(skill))
|
|
385
377
|
.filter((skill) => SUPPORT_GROUPS.has(skill) || !omit.has(skill));
|
|
386
|
-
const suffix = SKILL_TAIL.exec(command)?.[
|
|
378
|
+
const suffix = SKILL_TAIL.exec(command)?.[4] || '';
|
|
387
379
|
const bare = suffix ? command.slice(0, command.length - suffix.length) : command;
|
|
388
380
|
for (const skill of skills) {
|
|
389
381
|
const existing = plannedByMember.get(skill);
|
|
@@ -393,8 +385,6 @@ export function planBundleCommands(action, groupIds, {
|
|
|
393
385
|
}
|
|
394
386
|
let skillCommand = withSkills(bare, [skill]);
|
|
395
387
|
if (suffix && skill === RUNTIME_MEMBER) skillCommand = `${skillCommand}${suffix}`;
|
|
396
|
-
// The pinned CLI has no --symlink flag. --yes is its only supported way to select the
|
|
397
|
-
// default symlink deployment without presenting the install-method chooser.
|
|
398
388
|
skillCommand = withYesFlag(skillCommand);
|
|
399
389
|
const invocationPolicy = skill === 'tdd' && enableTdd
|
|
400
390
|
? undefined
|
|
@@ -409,55 +399,158 @@ export function planBundleCommands(action, groupIds, {
|
|
|
409
399
|
command: skillCommand,
|
|
410
400
|
cwd: localRoot,
|
|
411
401
|
failureMessage: step.onFailure.message,
|
|
402
|
+
...(memberMetadata.get(skill)?.requires?.length > 0
|
|
403
|
+
? { requires: memberMetadata.get(skill).requires }
|
|
404
|
+
: {}),
|
|
405
|
+
metadata: memberMetadata.get(skill) ?? { installName: skill },
|
|
412
406
|
};
|
|
413
407
|
plannedByMember.set(skill, entry);
|
|
414
408
|
planned.push(entry);
|
|
415
409
|
}
|
|
416
410
|
}
|
|
417
411
|
}
|
|
418
|
-
|
|
419
412
|
return planned;
|
|
420
413
|
}
|
|
421
414
|
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
415
|
+
export function coalesceBundleCommands(commands) {
|
|
416
|
+
const batches = [];
|
|
417
|
+
for (const planned of commands) {
|
|
418
|
+
const previous = batches.at(-1);
|
|
419
|
+
if (!previous || previous.sourceId !== planned.sourceId || previous.cwd !== planned.cwd) {
|
|
420
|
+
batches.push({
|
|
421
|
+
sourceId: planned.sourceId,
|
|
422
|
+
members: [planned],
|
|
423
|
+
command: planned.command,
|
|
424
|
+
cwd: planned.cwd,
|
|
425
|
+
failureMessage: planned.failureMessage,
|
|
426
|
+
});
|
|
427
|
+
continue;
|
|
428
|
+
}
|
|
429
|
+
previous.members.push(planned);
|
|
430
|
+
const suffix = previous.members.map(({ command }) => SKILL_TAIL.exec(command)?.[4] || '').find(Boolean) || '';
|
|
431
|
+
const bare = suffix && previous.command.endsWith(suffix)
|
|
432
|
+
? previous.command.slice(0, previous.command.length - suffix.length)
|
|
433
|
+
: previous.command;
|
|
434
|
+
previous.command = withSkills(bare, previous.members.map(({ member }) => member));
|
|
435
|
+
if (suffix) previous.command = `${previous.command}${suffix}`;
|
|
436
|
+
}
|
|
437
|
+
for (const batch of batches) {
|
|
438
|
+
batch.groupIds = [...new Set(batch.members.flatMap((member) => member.groupIds))];
|
|
439
|
+
batch.groupName = [...new Set(batch.members.map((member) => member.groupName))].join(', ');
|
|
440
|
+
}
|
|
441
|
+
return batches;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
// Source batches continue independently. Within each batch, command status is only process evidence;
|
|
445
|
+
// installed-content verification is the per-member authority used for attribution and memory.
|
|
446
|
+
export function runBundleCommands(batches, {
|
|
427
447
|
execute = (command, cwd) => spawnSync('/bin/bash', ['-lc', command], { cwd, stdio: 'inherit' }),
|
|
428
448
|
applyPolicy = applyInvocationPolicy,
|
|
449
|
+
verifyMember,
|
|
429
450
|
skillsRoot,
|
|
430
451
|
stdout = process.stdout,
|
|
431
452
|
stderr = process.stderr,
|
|
453
|
+
satisfiedMembers = [],
|
|
432
454
|
} = {}) {
|
|
433
455
|
const succeeded = [];
|
|
434
456
|
const failed = [];
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
const
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
457
|
+
const available = new Set(satisfiedMembers);
|
|
458
|
+
for (const candidate of batches) {
|
|
459
|
+
const legacySingle = !Array.isArray(candidate.members);
|
|
460
|
+
const batch = legacySingle ? { ...candidate, members: [candidate] } : candidate;
|
|
461
|
+
let runnable = [...batch.members];
|
|
462
|
+
let changed = true;
|
|
463
|
+
while (changed) {
|
|
464
|
+
changed = false;
|
|
465
|
+
const runnableNames = new Set(runnable.map(({ member }) => member));
|
|
466
|
+
for (const planned of [...runnable]) {
|
|
467
|
+
const blockedBy = (planned.requires ?? [])
|
|
468
|
+
.filter((dependency) => !available.has(dependency) && !runnableNames.has(dependency));
|
|
469
|
+
if (blockedBy.length === 0) continue;
|
|
470
|
+
stderr.write(`Dependency failed for ${planned.member}: ${blockedBy.join(', ')} is not available\n`);
|
|
471
|
+
failed.push({ planned, status: 1, blockedBy });
|
|
472
|
+
runnable = runnable.filter((candidate) => candidate !== planned);
|
|
473
|
+
changed = true;
|
|
474
|
+
}
|
|
475
|
+
}
|
|
476
|
+
if (runnable.length === 0) continue;
|
|
477
|
+
let command = batch.command;
|
|
478
|
+
let batchSuffix = '';
|
|
479
|
+
if (!legacySingle) {
|
|
480
|
+
batchSuffix = SKILL_TAIL.exec(batch.command)?.[4] || '';
|
|
481
|
+
command = withSkills(batch.command, runnable.map(({ member }) => member));
|
|
482
|
+
if (batchSuffix && !runnable.some(({ member }) => member === RUNTIME_MEMBER)) {
|
|
483
|
+
command = command.slice(0, command.length - batchSuffix.length);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
stdout.write(`\nInstalling ${runnable.map(({ member }) => member).join(', ')} (${batch.sourceId})...\n`);
|
|
487
|
+
const result = execute(command, batch.cwd);
|
|
488
|
+
const status = Number.isInteger(result.status) ? result.status : 1;
|
|
489
|
+
const policyFailures = new Set();
|
|
490
|
+
if (status === 0) {
|
|
491
|
+
for (const planned of runnable) {
|
|
492
|
+
if (!planned.invocationPolicy) continue;
|
|
493
|
+
try {
|
|
441
494
|
applyPolicy({
|
|
442
495
|
installName: planned.member,
|
|
443
496
|
policy: planned.invocationPolicy,
|
|
444
497
|
...(skillsRoot ? { skillsRoot } : {}),
|
|
445
498
|
});
|
|
499
|
+
} catch (error) {
|
|
500
|
+
stderr.write(`Invocation policy failed for ${planned.member}: ${error.message}\n`);
|
|
501
|
+
policyFailures.add(planned.member);
|
|
446
502
|
}
|
|
447
|
-
|
|
503
|
+
}
|
|
504
|
+
} else {
|
|
505
|
+
stderr.write(`${batch.failureMessage}\n`);
|
|
506
|
+
}
|
|
507
|
+
for (const planned of runnable) {
|
|
508
|
+
if (policyFailures.has(planned.member)) {
|
|
509
|
+
failed.push({ planned, status: 1 });
|
|
510
|
+
continue;
|
|
511
|
+
}
|
|
512
|
+
if (status !== 0 && batchSuffix && planned.member === RUNTIME_MEMBER) {
|
|
513
|
+
stderr.write(`Post-install command failed for ${planned.member}\n`);
|
|
514
|
+
failed.push({ planned, status });
|
|
448
515
|
continue;
|
|
516
|
+
}
|
|
517
|
+
try {
|
|
518
|
+
const verified = verifyMember ? verifyMember(planned) : status === 0;
|
|
519
|
+
if (verified) {
|
|
520
|
+
succeeded.push(planned);
|
|
521
|
+
available.add(planned.member);
|
|
522
|
+
} else {
|
|
523
|
+
stderr.write(`Post-install verification failed for ${planned.member}\n`);
|
|
524
|
+
failed.push({ planned, status: status || 1 });
|
|
525
|
+
}
|
|
449
526
|
} catch (error) {
|
|
450
|
-
stderr.write(`
|
|
527
|
+
stderr.write(`Post-install verification failed for ${planned.member}: ${error.message}\n`);
|
|
451
528
|
failed.push({ planned, status: 1 });
|
|
452
|
-
continue;
|
|
453
529
|
}
|
|
454
530
|
}
|
|
455
|
-
stderr.write(`${planned.failureMessage}\n`);
|
|
456
|
-
failed.push({ planned, status: Number.isInteger(result.status) ? result.status : 1 });
|
|
457
531
|
}
|
|
458
532
|
return { succeeded, failed };
|
|
459
533
|
}
|
|
460
534
|
|
|
535
|
+
export function enforceReadyDependencies(readyMembers, records, stderr = process.stderr) {
|
|
536
|
+
const metadata = new Map(records.map((record) => [record.member, record]));
|
|
537
|
+
const blocked = [];
|
|
538
|
+
let changed = true;
|
|
539
|
+
while (changed) {
|
|
540
|
+
changed = false;
|
|
541
|
+
for (const member of [...readyMembers]) {
|
|
542
|
+
const record = metadata.get(member);
|
|
543
|
+
const blockedBy = (record?.requires ?? []).filter((dependency) => !readyMembers.has(dependency));
|
|
544
|
+
if (blockedBy.length === 0) continue;
|
|
545
|
+
readyMembers.delete(member);
|
|
546
|
+
blocked.push({ planned: record, status: 1, blockedBy });
|
|
547
|
+
stderr.write(`Dependency unavailable for ${member}: ${blockedBy.join(', ')} is not ready\n`);
|
|
548
|
+
changed = true;
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
return blocked;
|
|
552
|
+
}
|
|
553
|
+
|
|
461
554
|
// The four run buckets, printed after every non-dry run. `unchanged` (digest-identical skip, #1127)
|
|
462
555
|
// and `declined` (new-member consent, #1128) render from empty lists until those units land — the
|
|
463
556
|
// summary contract is stable from here on, so both buckets are always shown.
|
|
@@ -508,7 +601,12 @@ export function expectedMemberDigest(source, member, { localRoot }) {
|
|
|
508
601
|
// installed store (`~/.agents/skills`); the member lives under it by installName.
|
|
509
602
|
export function classifyInstalledMember(source, member, { enableTdd = true, localRoot, skillsRoot }) {
|
|
510
603
|
const directory = join(skillsRoot, member.installName);
|
|
511
|
-
const entry = {
|
|
604
|
+
const entry = {
|
|
605
|
+
member: member.installName,
|
|
606
|
+
sourceId: source?.id ?? null,
|
|
607
|
+
directory,
|
|
608
|
+
...(member.requires?.length > 0 ? { requires: member.requires } : {}),
|
|
609
|
+
};
|
|
512
610
|
if (!statSync(directory, { throwIfNoEntry: false })?.isDirectory()) {
|
|
513
611
|
return { ...entry, status: 'missing' };
|
|
514
612
|
}
|
|
@@ -835,6 +933,7 @@ function selectionIncludesTdd(groupIds, catalog) {
|
|
|
835
933
|
export async function runCli(args, {
|
|
836
934
|
execute,
|
|
837
935
|
applyPolicy,
|
|
936
|
+
verifyMember,
|
|
838
937
|
stdin = process.stdin,
|
|
839
938
|
stdout = process.stdout,
|
|
840
939
|
stderr = process.stderr,
|
|
@@ -930,10 +1029,11 @@ export async function runCli(args, {
|
|
|
930
1029
|
skipMembers,
|
|
931
1030
|
omitMembers: declined,
|
|
932
1031
|
});
|
|
1032
|
+
const batches = coalesceBundleCommands(commands);
|
|
933
1033
|
if (parsed.dryRun) {
|
|
934
1034
|
if (buckets) printUpdateBuckets(stdout, buckets);
|
|
935
|
-
for (const
|
|
936
|
-
stdout.write(`\n${
|
|
1035
|
+
for (const batch of batches) {
|
|
1036
|
+
stdout.write(`\n${batch.groupName} / ${batch.sourceId} / ${batch.members.map(({ member }) => member).join(', ')}\n${batch.command}\n`);
|
|
937
1037
|
}
|
|
938
1038
|
if (buckets && commands.length === 0 && archiveCandidates.length === 0) {
|
|
939
1039
|
stdout.write('Nothing to do: every accepted skill is already up to date.\n');
|
|
@@ -946,24 +1046,43 @@ export async function runCli(args, {
|
|
|
946
1046
|
const skipping = buckets.unchanged.map((entry) => entry.member).filter((member) => !SUPPORT_GROUPS.has(member));
|
|
947
1047
|
if (skipping.length > 0) stdout.write(`Up to date, skipping: ${skipping.join(', ')}\n`);
|
|
948
1048
|
}
|
|
949
|
-
const
|
|
1049
|
+
const verificationTransport = resolveBundleTransport(transport);
|
|
1050
|
+
const verificationSources = new Map(readRecipes(verificationTransport.recipesPath).sources
|
|
1051
|
+
.map((source) => [source.id, source]));
|
|
1052
|
+
const verifyInstalledMember = verifyMember ?? (execute ? undefined : (planned) => {
|
|
1053
|
+
const source = verificationSources.get(planned.sourceId);
|
|
1054
|
+
return classifyInstalledMember(source, planned.metadata, {
|
|
1055
|
+
enableTdd,
|
|
1056
|
+
localRoot: verificationTransport.localRoot,
|
|
1057
|
+
skillsRoot,
|
|
1058
|
+
}).status === 'unchanged';
|
|
1059
|
+
});
|
|
1060
|
+
const { succeeded, failed } = runBundleCommands(batches, {
|
|
950
1061
|
execute,
|
|
951
1062
|
applyPolicy,
|
|
1063
|
+
verifyMember: verifyInstalledMember,
|
|
952
1064
|
skillsRoot,
|
|
953
1065
|
stdout,
|
|
954
1066
|
stderr,
|
|
1067
|
+
satisfiedMembers: buckets?.unchanged.map((entry) => entry.member),
|
|
955
1068
|
});
|
|
956
1069
|
const readyMembers = new Set([
|
|
957
1070
|
...succeeded.map((planned) => planned.member),
|
|
958
1071
|
...(buckets?.unchanged.map((entry) => entry.member) ?? []),
|
|
959
1072
|
]);
|
|
1073
|
+
failed.push(...enforceReadyDependencies(
|
|
1074
|
+
readyMembers,
|
|
1075
|
+
[...commands, ...(buckets?.unchanged ?? [])],
|
|
1076
|
+
stderr,
|
|
1077
|
+
));
|
|
1078
|
+
const coherentSucceeded = succeeded.filter((planned) => readyMembers.has(planned.member));
|
|
960
1079
|
const archives = runArchiveCommands(
|
|
961
1080
|
archiveCandidates.filter((candidate) => !declinedArchives.has(candidate.installName)),
|
|
962
1081
|
readyMembers,
|
|
963
1082
|
{ execute, skillsRoot, stdout, stderr },
|
|
964
1083
|
);
|
|
965
1084
|
printRunSummary(stdout, {
|
|
966
|
-
succeeded,
|
|
1085
|
+
succeeded: coherentSucceeded,
|
|
967
1086
|
failed,
|
|
968
1087
|
unchanged: buckets?.unchanged.map((entry) => entry.member),
|
|
969
1088
|
declined: [...declined],
|
|
@@ -154,6 +154,10 @@ const TEST_FIXTURE = /\.fixtures?\.[^.]+$/;
|
|
|
154
154
|
export function publishedContentExcluded(name) {
|
|
155
155
|
return name.startsWith('.')
|
|
156
156
|
|| name === 'node_modules'
|
|
157
|
+
|| name === '__pycache__'
|
|
158
|
+
|| name === 'evals'
|
|
159
|
+
|| name === 'samples'
|
|
160
|
+
|| name === 'tests'
|
|
157
161
|
|| name.endsWith('-workspace')
|
|
158
162
|
|| name.endsWith('.test.mjs')
|
|
159
163
|
|| TEST_FIXTURE.test(name);
|