@nebulacomponents/citable 1.9.0 → 1.10.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.
Files changed (84) hide show
  1. package/CHANGELOG.md +10 -0
  2. package/README.md +4 -2
  3. package/dist/universal/.agents/skills/citable/SKILL.md +5 -2
  4. package/dist/universal/.agents/skills/citable/VERSION +1 -1
  5. package/dist/universal/.agents/skills/citable/commands/README.md +1 -0
  6. package/dist/universal/.agents/skills/citable/commands/delivery.md +18 -0
  7. package/dist/universal/.agents/skills/citable/manifest.json +8 -6
  8. package/dist/universal/.agents/skills/citable/schemas/schedule.schema.json +4 -0
  9. package/dist/universal/.claude/skills/citable/SKILL.md +5 -2
  10. package/dist/universal/.claude/skills/citable/VERSION +1 -1
  11. package/dist/universal/.claude/skills/citable/commands/README.md +1 -0
  12. package/dist/universal/.claude/skills/citable/commands/delivery.md +18 -0
  13. package/dist/universal/.claude/skills/citable/manifest.json +8 -6
  14. package/dist/universal/.claude/skills/citable/schemas/schedule.schema.json +4 -0
  15. package/dist/universal/.cursor/skills/citable/SKILL.md +5 -2
  16. package/dist/universal/.cursor/skills/citable/VERSION +1 -1
  17. package/dist/universal/.cursor/skills/citable/commands/README.md +1 -0
  18. package/dist/universal/.cursor/skills/citable/commands/delivery.md +18 -0
  19. package/dist/universal/.cursor/skills/citable/manifest.json +8 -6
  20. package/dist/universal/.cursor/skills/citable/schemas/schedule.schema.json +4 -0
  21. package/dist/universal/.gemini/skills/citable/SKILL.md +5 -2
  22. package/dist/universal/.gemini/skills/citable/VERSION +1 -1
  23. package/dist/universal/.gemini/skills/citable/commands/README.md +1 -0
  24. package/dist/universal/.gemini/skills/citable/commands/delivery.md +18 -0
  25. package/dist/universal/.gemini/skills/citable/manifest.json +8 -6
  26. package/dist/universal/.gemini/skills/citable/schemas/schedule.schema.json +4 -0
  27. package/dist/universal/.github/skills/citable/SKILL.md +5 -2
  28. package/dist/universal/.github/skills/citable/VERSION +1 -1
  29. package/dist/universal/.github/skills/citable/commands/README.md +1 -0
  30. package/dist/universal/.github/skills/citable/commands/delivery.md +18 -0
  31. package/dist/universal/.github/skills/citable/manifest.json +8 -6
  32. package/dist/universal/.github/skills/citable/schemas/schedule.schema.json +4 -0
  33. package/dist/universal/.kiro/skills/citable/SKILL.md +5 -2
  34. package/dist/universal/.kiro/skills/citable/VERSION +1 -1
  35. package/dist/universal/.kiro/skills/citable/commands/README.md +1 -0
  36. package/dist/universal/.kiro/skills/citable/commands/delivery.md +18 -0
  37. package/dist/universal/.kiro/skills/citable/manifest.json +8 -6
  38. package/dist/universal/.kiro/skills/citable/schemas/schedule.schema.json +4 -0
  39. package/dist/universal/.opencode/skills/citable/SKILL.md +5 -2
  40. package/dist/universal/.opencode/skills/citable/VERSION +1 -1
  41. package/dist/universal/.opencode/skills/citable/commands/README.md +1 -0
  42. package/dist/universal/.opencode/skills/citable/commands/delivery.md +18 -0
  43. package/dist/universal/.opencode/skills/citable/manifest.json +8 -6
  44. package/dist/universal/.opencode/skills/citable/schemas/schedule.schema.json +4 -0
  45. package/dist/universal/.pi/agent/skills/citable/SKILL.md +5 -2
  46. package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
  47. package/dist/universal/.pi/agent/skills/citable/commands/README.md +1 -0
  48. package/dist/universal/.pi/agent/skills/citable/commands/delivery.md +18 -0
  49. package/dist/universal/.pi/agent/skills/citable/manifest.json +8 -6
  50. package/dist/universal/.pi/agent/skills/citable/schemas/schedule.schema.json +4 -0
  51. package/dist/universal/.qoder/skills/citable/SKILL.md +5 -2
  52. package/dist/universal/.qoder/skills/citable/VERSION +1 -1
  53. package/dist/universal/.qoder/skills/citable/commands/README.md +1 -0
  54. package/dist/universal/.qoder/skills/citable/commands/delivery.md +18 -0
  55. package/dist/universal/.qoder/skills/citable/manifest.json +8 -6
  56. package/dist/universal/.qoder/skills/citable/schemas/schedule.schema.json +4 -0
  57. package/dist/universal/.rovodev/skills/citable/SKILL.md +5 -2
  58. package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
  59. package/dist/universal/.rovodev/skills/citable/commands/README.md +1 -0
  60. package/dist/universal/.rovodev/skills/citable/commands/delivery.md +18 -0
  61. package/dist/universal/.rovodev/skills/citable/manifest.json +8 -6
  62. package/dist/universal/.rovodev/skills/citable/schemas/schedule.schema.json +4 -0
  63. package/dist/universal/.trae/skills/citable/SKILL.md +5 -2
  64. package/dist/universal/.trae/skills/citable/VERSION +1 -1
  65. package/dist/universal/.trae/skills/citable/commands/README.md +1 -0
  66. package/dist/universal/.trae/skills/citable/commands/delivery.md +18 -0
  67. package/dist/universal/.trae/skills/citable/manifest.json +8 -6
  68. package/dist/universal/.trae/skills/citable/schemas/schedule.schema.json +4 -0
  69. package/dist/universal/.trae-cn/skills/citable/SKILL.md +5 -2
  70. package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
  71. package/dist/universal/.trae-cn/skills/citable/commands/README.md +1 -0
  72. package/dist/universal/.trae-cn/skills/citable/commands/delivery.md +18 -0
  73. package/dist/universal/.trae-cn/skills/citable/manifest.json +8 -6
  74. package/dist/universal/.trae-cn/skills/citable/schemas/schedule.schema.json +4 -0
  75. package/dist/universal/manifest.json +99 -75
  76. package/package.json +1 -1
  77. package/schemas/schedule.schema.json +4 -0
  78. package/skill/SKILL.md +5 -2
  79. package/skill/commands/README.md +1 -0
  80. package/skill/commands/delivery.md +18 -0
  81. package/src/cli/index.js +15 -0
  82. package/src/commands/compareSnapshots.js +15 -0
  83. package/src/commands/delivery.js +50 -0
  84. package/src/registries/index.js +1 -0
package/src/cli/index.js CHANGED
@@ -19,6 +19,7 @@ import { evaluateObjective, importMetrics, initializeObjective, validateObjectiv
19
19
  import { configureConnection, connectionStatus, discoverConnections, disconnectConnection, syncConnection, validateConnection } from '../commands/connect.js';
20
20
  import { evaluateDispositions, validateGovernance } from '../commands/governance.js';
21
21
  import { evaluateReviews, initializeSamplingPlan, prioritizeReviews, queueReviews, selectSample } from '../commands/reviews.js';
22
+ import { projectGithub, runSchedule } from '../commands/delivery.js';
22
23
 
23
24
  const HELP = `citable — SEO / AEO / GEO audit, remediation, validation, and governance
24
25
 
@@ -62,6 +63,8 @@ Commands
62
63
  reviews plan Validate/add a sampling plan from --input
63
64
  reviews sample [plan] Select a reproducible census or seeded random sample
64
65
  reviews evaluate Detect stale decisions and require disagreement adjudication
66
+ schedules run [id] Execute an active version-pinned audit schedule
67
+ project github [run] Render non-authoritative GitHub annotations from a run
65
68
  self-upgrade Check for a newer version and upgrade the npx cache
66
69
 
67
70
  Options
@@ -272,6 +275,18 @@ export async function main(argv = process.argv.slice(2), options = {}) {
272
275
  out(args,`reviews ${mode}: ${r.created?.length ?? r.items?.length ?? r.selected_item_ids?.length ?? r.results?.length ?? 1} item(s)${args.write?' written':' (dry run)'}`,r);
273
276
  break;
274
277
  }
278
+ case 'schedules': {
279
+ if(args._[0]!=='run') throw new Error('usage: citable schedules run <schedule-id> [--ref-date YYYY-MM-DD]');
280
+ const r=await runSchedule(root,{scheduleId:args._[1],refDate:args.refDate});
281
+ out(args,`schedule ${r.schedule_execution.schedule_id}: audit ${r.runId}\nEvidence package: ${r.dir}\nExecution record: ${r.execution_file}`,r);
282
+ break;
283
+ }
284
+ case 'project': {
285
+ if(args._[0]!=='github') throw new Error('usage: citable project github <run-id>');
286
+ const r=projectGithub(root,{runId:args._[1]});
287
+ out(args,`project github ${r.source_run_id}: ${r.annotations.length} annotation(s)\nProjection: ${path.join(r.dir,'annotations.json')}`,r);
288
+ break;
289
+ }
275
290
  case 'self-upgrade': {
276
291
  const output = await selfUpgradeCommand(argv.slice(1));
277
292
  console.log(output);
@@ -27,6 +27,16 @@ export function compareSnapshots(root, { runA, runB } = {}) {
27
27
  };
28
28
  const a = load(runA);
29
29
  const b = load(runB);
30
+ const changedKeys = (left = {}, right = {}) => [...new Set([...Object.keys(left), ...Object.keys(right)])].filter((key) => left[key] !== right[key]);
31
+ const changeDimensions = {
32
+ resource_changed: changedKeys(a.manifest.input_hashes, b.manifest.input_hashes),
33
+ evidence_artifacts_changed: changedKeys(a.manifest.output_hashes, b.manifest.output_hashes),
34
+ detector_set_changed: JSON.stringify(a.manifest.detectors_run) !== JSON.stringify(b.manifest.detectors_run),
35
+ configuration_changed: a.manifest.configuration_hash !== b.manifest.configuration_hash,
36
+ observation_method_changed: a.manifest.command !== b.manifest.command || JSON.stringify(a.manifest.argv) !== JSON.stringify(b.manifest.argv) || a.manifest.target?.kind !== b.manifest.target?.kind,
37
+ tool_changed: a.manifest.tool_version !== b.manifest.tool_version,
38
+ external_system_may_have_changed: a.manifest.target?.kind === 'url' || b.manifest.target?.kind === 'url',
39
+ };
30
40
  const key = (f) => `${f.detector_id}|${f.subject.identifier}|${f.observation.summary}`;
31
41
  const aKeys = new Map(a.findings.map((f) => [key(f), f]));
32
42
  const bKeys = new Map(b.findings.map((f) => [key(f), f]));
@@ -38,6 +48,11 @@ export function compareSnapshots(root, { runA, runB } = {}) {
38
48
  baseline_timestamp: a.manifest.timestamp,
39
49
  comparison_timestamp: b.manifest.timestamp,
40
50
  regressions, resolved, persisting,
51
+ comparability: {
52
+ comparable: !changeDimensions.detector_set_changed && !changeDimensions.configuration_changed && !changeDimensions.observation_method_changed && !changeDimensions.tool_changed,
53
+ change_dimensions: changeDimensions,
54
+ limitation: 'These dimensions identify observed differences between run envelopes; they do not establish what caused a finding change.',
55
+ },
41
56
  summary: {
42
57
  new_findings: regressions.length,
43
58
  resolved_findings: resolved.length,
@@ -0,0 +1,50 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { audit } from './audit.js';
5
+ import { loadRegistries } from '../registries/index.js';
6
+ import { readJson, sha256, writeJson, nowIso } from '../shared/io.js';
7
+
8
+ const PKG = readJson(path.resolve(path.dirname(fileURLToPath(import.meta.url)), '../../package.json'));
9
+
10
+ export async function runSchedule(root, { scheduleId, refDate } = {}) {
11
+ const { registries, problems } = loadRegistries(root);
12
+ if (problems.length) throw new Error(`registry validation failed: ${problems.join('; ')}`);
13
+ const schedule = registries.schedules.entries.find((item) => item.schedule_id === scheduleId);
14
+ if (!schedule) throw new Error(`schedule not found: ${scheduleId}`);
15
+ if (schedule.status !== 'active') throw new Error(`schedule ${scheduleId} is ${schedule.status}`);
16
+ if (schedule.expected_tool_version !== PKG.version) throw new Error(`schedule ${scheduleId} expects Citable ${schedule.expected_tool_version}, current version is ${PKG.version}`);
17
+ const result = await audit(root, { target: schedule.audit.target, scope: schedule.audit.scope, baseUrl: schedule.audit.base_url, refDate });
18
+ const execution = { schedule_id: scheduleId, cron: schedule.cron, owner: schedule.owner, executed_at: nowIso(), run_id: result.runId, manifest_hash: sha256(fs.readFileSync(path.join(result.dir, 'manifest.json'))), limitations: schedule.limitations };
19
+ const executionFile = path.join(root, '.citable', 'schedule-executions', `${result.runId}.json`);
20
+ writeJson(executionFile, execution);
21
+ return { ...result, schedule_execution: execution, execution_file: executionFile };
22
+ }
23
+
24
+ function annotationLevel(severity) {
25
+ if (['critical', 'high'].includes(severity)) return 'failure';
26
+ if (severity === 'medium') return 'warning';
27
+ return 'notice';
28
+ }
29
+
30
+ export function projectGithub(root, { runId } = {}) {
31
+ if (!runId) throw new Error('projection requires a source run id');
32
+ const runDir = path.join(root, '.citable', 'runs', runId);
33
+ const findingsFile = path.join(runDir, 'findings.json');
34
+ const manifestFile = path.join(runDir, 'manifest.json');
35
+ if (!fs.existsSync(findingsFile) || !fs.existsSync(manifestFile)) throw new Error(`run ${runId} is missing findings or manifest evidence`);
36
+ const findingsBytes = fs.readFileSync(findingsFile);
37
+ const manifestBytes = fs.readFileSync(manifestFile);
38
+ const annotations = JSON.parse(findingsBytes).map((finding) => ({
39
+ level: annotationLevel(finding.classification.severity),
40
+ title: `${finding.detector_id}: ${finding.classification.severity}`,
41
+ message: finding.observation.summary,
42
+ path: finding.subject.source_file || null,
43
+ line: finding.subject.source_location ? Number(String(finding.subject.source_location).match(/\d+/)?.[0]) || null : null,
44
+ finding_id: finding.finding_id,
45
+ }));
46
+ const projection = { source_run_id: runId, source_manifest_hash: sha256(manifestBytes), source_findings_hash: sha256(findingsBytes), generated_at: nowIso(), projection: 'github-check-annotations', authoritative: false, annotations };
47
+ const dir = path.join(root, '.citable', 'projections', 'github', runId);
48
+ writeJson(path.join(dir, 'annotations.json'), projection);
49
+ return { ...projection, dir };
50
+ }
@@ -22,6 +22,7 @@ export const REGISTRY_SPECS = [
22
22
  { file: 'exceptions.yaml', kind: 'exceptions', schema: 'exception.schema.json', idField: 'exception_id' },
23
23
  { file: 'review-items.yaml', kind: 'review_items', schema: 'review-item.schema.json', idField: 'review_item_id' },
24
24
  { file: 'sampling-plans.yaml', kind: 'sampling_plans', schema: 'sampling-plan.schema.json', idField: 'sampling_plan_id' },
25
+ { file: 'schedules.yaml', kind: 'schedules', schema: 'schedule.schema.json', idField: 'schedule_id' },
25
26
  ];
26
27
 
27
28
  export function contextDir(root) {