@nebulacomponents/citable 1.13.1 → 1.15.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 +312 -0
- package/README.md +44 -6
- package/dist/universal/.agents/skills/citable/SKILL.md +22 -8
- package/dist/universal/.agents/skills/citable/VERSION +1 -1
- package/dist/universal/.agents/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.agents/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.agents/skills/citable/commands/README.md +4 -1
- package/dist/universal/.agents/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.agents/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.agents/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.agents/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.agents/skills/citable/manifest.json +29 -16
- package/dist/universal/.agents/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.agents/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.agents/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.agents/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.agents/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.agents/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.agents/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.agents/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.agents/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.agents/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.agents/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.agents/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.agents/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.claude/agents/citable/citable-auditor.md +64 -0
- package/dist/universal/.claude/agents/citable/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.claude/agents/citable/manifest.json +12 -0
- package/dist/universal/.claude/skills/citable/SKILL.md +22 -8
- package/dist/universal/.claude/skills/citable/VERSION +1 -1
- package/dist/universal/.claude/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.claude/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.claude/skills/citable/commands/README.md +4 -1
- package/dist/universal/.claude/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.claude/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.claude/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.claude/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.claude/skills/citable/manifest.json +34 -16
- package/dist/universal/.claude/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.claude/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.claude/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.claude/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.claude/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.claude/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.claude/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.claude/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.claude/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.claude/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.claude/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.claude/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.claude/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.cursor/skills/citable/SKILL.md +22 -8
- package/dist/universal/.cursor/skills/citable/VERSION +1 -1
- package/dist/universal/.cursor/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.cursor/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.cursor/skills/citable/commands/README.md +4 -1
- package/dist/universal/.cursor/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.cursor/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.cursor/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.cursor/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.cursor/skills/citable/manifest.json +29 -16
- package/dist/universal/.cursor/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.cursor/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.cursor/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.cursor/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.cursor/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.cursor/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.cursor/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.cursor/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.cursor/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.cursor/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.cursor/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.cursor/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.cursor/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.gemini/skills/citable/SKILL.md +22 -8
- package/dist/universal/.gemini/skills/citable/VERSION +1 -1
- package/dist/universal/.gemini/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.gemini/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.gemini/skills/citable/commands/README.md +4 -1
- package/dist/universal/.gemini/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.gemini/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.gemini/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.gemini/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.gemini/skills/citable/manifest.json +29 -16
- package/dist/universal/.gemini/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.gemini/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.gemini/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.gemini/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.gemini/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.gemini/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.gemini/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.gemini/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.gemini/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.gemini/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.gemini/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.gemini/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.gemini/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.github/skills/citable/SKILL.md +22 -8
- package/dist/universal/.github/skills/citable/VERSION +1 -1
- package/dist/universal/.github/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.github/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.github/skills/citable/commands/README.md +4 -1
- package/dist/universal/.github/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.github/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.github/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.github/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.github/skills/citable/manifest.json +29 -16
- package/dist/universal/.github/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.github/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.github/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.github/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.github/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.github/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.github/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.github/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.github/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.github/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.github/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.github/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.github/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.kiro/skills/citable/SKILL.md +22 -8
- package/dist/universal/.kiro/skills/citable/VERSION +1 -1
- package/dist/universal/.kiro/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.kiro/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.kiro/skills/citable/commands/README.md +4 -1
- package/dist/universal/.kiro/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.kiro/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.kiro/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.kiro/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.kiro/skills/citable/manifest.json +29 -16
- package/dist/universal/.kiro/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.kiro/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.kiro/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.kiro/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.kiro/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.kiro/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.kiro/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.kiro/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.kiro/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.kiro/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.kiro/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.kiro/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.kiro/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.opencode/skills/citable/SKILL.md +22 -8
- package/dist/universal/.opencode/skills/citable/VERSION +1 -1
- package/dist/universal/.opencode/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.opencode/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.opencode/skills/citable/commands/README.md +4 -1
- package/dist/universal/.opencode/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.opencode/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.opencode/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.opencode/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.opencode/skills/citable/manifest.json +29 -16
- package/dist/universal/.opencode/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.opencode/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.opencode/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.opencode/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.opencode/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.opencode/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.opencode/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.opencode/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.opencode/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.opencode/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.opencode/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.opencode/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.opencode/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.pi/agent/skills/citable/SKILL.md +22 -8
- package/dist/universal/.pi/agent/skills/citable/VERSION +1 -1
- package/dist/universal/.pi/agent/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.pi/agent/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.pi/agent/skills/citable/commands/README.md +4 -1
- package/dist/universal/.pi/agent/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.pi/agent/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.pi/agent/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.pi/agent/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.pi/agent/skills/citable/manifest.json +29 -16
- package/dist/universal/.pi/agent/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.pi/agent/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.pi/agent/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.pi/agent/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.qoder/skills/citable/SKILL.md +22 -8
- package/dist/universal/.qoder/skills/citable/VERSION +1 -1
- package/dist/universal/.qoder/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.qoder/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.qoder/skills/citable/commands/README.md +4 -1
- package/dist/universal/.qoder/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.qoder/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.qoder/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.qoder/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.qoder/skills/citable/manifest.json +29 -16
- package/dist/universal/.qoder/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.qoder/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.qoder/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.qoder/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.qoder/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.qoder/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.qoder/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.qoder/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.qoder/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.qoder/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.qoder/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.qoder/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.qoder/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.rovodev/skills/citable/SKILL.md +22 -8
- package/dist/universal/.rovodev/skills/citable/VERSION +1 -1
- package/dist/universal/.rovodev/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.rovodev/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.rovodev/skills/citable/commands/README.md +4 -1
- package/dist/universal/.rovodev/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.rovodev/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.rovodev/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.rovodev/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.rovodev/skills/citable/manifest.json +29 -16
- package/dist/universal/.rovodev/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.rovodev/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.rovodev/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.rovodev/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.rovodev/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.rovodev/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.rovodev/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.rovodev/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.rovodev/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.rovodev/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.rovodev/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.rovodev/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.rovodev/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.trae/skills/citable/SKILL.md +22 -8
- package/dist/universal/.trae/skills/citable/VERSION +1 -1
- package/dist/universal/.trae/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.trae/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.trae/skills/citable/commands/README.md +4 -1
- package/dist/universal/.trae/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.trae/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.trae/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.trae/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.trae/skills/citable/manifest.json +29 -16
- package/dist/universal/.trae/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.trae/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.trae/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.trae/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.trae/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.trae/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.trae/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.trae/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.trae/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.trae/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.trae/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.trae/skills/citable/scripts/README.md +3 -1
- package/dist/universal/.trae-cn/skills/citable/SKILL.md +22 -8
- package/dist/universal/.trae-cn/skills/citable/VERSION +1 -1
- package/dist/universal/.trae-cn/skills/citable/agents/citable-auditor.md +64 -0
- package/dist/universal/.trae-cn/skills/citable/agents/citable-semantic-reviewer.md +66 -0
- package/dist/universal/.trae-cn/skills/citable/commands/README.md +4 -1
- package/dist/universal/.trae-cn/skills/citable/commands/action-findings.md +9 -1
- package/dist/universal/.trae-cn/skills/citable/commands/observe-and-act.md +62 -8
- package/dist/universal/.trae-cn/skills/citable/commands/plan-audit.md +35 -0
- package/dist/universal/.trae-cn/skills/citable/commands/reporting.md +57 -0
- package/dist/universal/.trae-cn/skills/citable/manifest.json +29 -16
- package/dist/universal/.trae-cn/skills/citable/schemas/action-plan.schema.json +13 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/browser-evidence-plan.schema.json +14 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/canonical-index-import.schema.json +30 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/common.defs.json +12 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/corroboration-import.schema.json +48 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/entity-graph.schema.json +75 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/entity.schema.json +13 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/observation.schema.json +1 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/page.schema.json +3 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/regional-network-import.schema.json +55 -0
- package/dist/universal/.trae-cn/skills/citable/schemas/schedule.schema.json +1 -1
- package/dist/universal/.trae-cn/skills/citable/schemas/source-identity-chain.schema.json +83 -0
- package/dist/universal/.trae-cn/skills/citable/scripts/README.md +3 -1
- package/dist/universal/manifest.json +306 -195
- package/package.json +1 -1
- package/schemas/action-plan.schema.json +13 -0
- package/schemas/browser-evidence-plan.schema.json +14 -0
- package/schemas/canonical-index-import.schema.json +30 -0
- package/schemas/common.defs.json +12 -1
- package/schemas/corroboration-import.schema.json +48 -0
- package/schemas/entity-graph.schema.json +75 -0
- package/schemas/entity.schema.json +13 -0
- package/schemas/observation.schema.json +1 -1
- package/schemas/page.schema.json +3 -0
- package/schemas/regional-network-import.schema.json +55 -0
- package/schemas/schedule.schema.json +1 -1
- package/schemas/source-identity-chain.schema.json +83 -0
- package/scripts/build-dist.js +36 -3
- package/skill/SKILL.md +22 -8
- package/skill/agents/citable-auditor.md +64 -0
- package/skill/agents/citable-semantic-reviewer.md +66 -0
- package/skill/commands/README.md +4 -1
- package/skill/commands/action-findings.md +9 -1
- package/skill/commands/observe-and-act.md +62 -8
- package/skill/commands/plan-audit.md +35 -0
- package/skill/commands/reporting.md +57 -0
- package/src/cli/index.js +115 -15
- package/src/commands/actionPlan.js +12 -1
- package/src/commands/audit.js +4 -0
- package/src/commands/connect.js +29 -0
- package/src/commands/context.js +15 -0
- package/src/commands/delivery.js +86 -2
- package/src/commands/demo.js +36 -0
- package/src/commands/fixtures/demo/.citable/claims.yaml +27 -0
- package/src/commands/fixtures/demo/.citable/competitors.yaml +3 -0
- package/src/commands/fixtures/demo/.citable/config.yaml +18 -0
- package/src/commands/fixtures/demo/.citable/crawlers.yaml +30 -0
- package/src/commands/fixtures/demo/.citable/entities.yaml +28 -0
- package/src/commands/fixtures/demo/.citable/evidence.yaml +19 -0
- package/src/commands/fixtures/demo/.citable/experiments.yaml +3 -0
- package/src/commands/fixtures/demo/.citable/pages.yaml +54 -0
- package/src/commands/fixtures/demo/.citable/prompts.yaml +11 -0
- package/src/commands/fixtures/demo/.citable/queries.yaml +10 -0
- package/src/commands/fixtures/demo/site/index.html +12 -0
- package/src/commands/fixtures/demo/site/learn/execution-governance/index.html +14 -0
- package/src/commands/fixtures/demo/site/pricing/index.html +10 -0
- package/src/commands/fixtures/demo/site/products/gatekeeper/index.html +15 -0
- package/src/commands/fixtures/demo/site/robots.txt +7 -0
- package/src/commands/fixtures/demo/site/sitemap.xml +7 -0
- package/src/commands/init.js +5 -2
- package/src/commands/monitor.js +35 -0
- package/src/commands/observe.js +315 -15
- package/src/commands/planAudit.js +151 -0
- package/src/commands/reportDashboard.js +21 -0
- package/src/commands/reportShareOfVoice.js +60 -0
- package/src/commands/schemaCmd.js +5 -1
- package/src/commands/substantiate.js +22 -10
- package/src/connectors/http.js +6 -3
- package/src/connectors/index.js +3 -1
- package/src/connectors/webflow.js +224 -0
- package/src/connectors/wordpress.js +226 -0
- package/src/crawler/fetch.js +34 -6
- package/src/detectors/geoReco.js +101 -0
- package/src/extractor/page.js +17 -0
- package/src/installer/index.js +282 -28
- package/src/installer/providers.js +11 -0
- package/src/monitoring/alertDelivery.js +98 -0
- package/src/observations/browserJourney.js +15 -7
- package/src/observations/claimDiff.js +83 -0
- package/src/observations/common.js +25 -0
- package/src/observations/entityGraph.js +89 -0
- package/src/observations/renderParity.js +49 -0
- package/src/observations/sourceIdentity.js +62 -0
- package/src/observations/stance.js +261 -0
- package/src/registries/seed.js +95 -0
- package/src/registries/seeds/saas-pricing/claims.yaml +18 -0
- package/src/registries/seeds/saas-pricing/entities.yaml +20 -0
- package/src/registries/seeds/saas-pricing/seed.json +6 -0
- package/src/reporting/dashboard.js +254 -0
- package/src/reporting/shareOfVoice.js +362 -0
- package/src/shared/checklist.js +19 -0
package/src/crawler/fetch.js
CHANGED
|
@@ -4,9 +4,11 @@ import net from 'node:net';
|
|
|
4
4
|
const blockedAddresses = new net.BlockList();
|
|
5
5
|
for (const [network, prefix] of [
|
|
6
6
|
['0.0.0.0', 8], ['10.0.0.0', 8], ['100.64.0.0', 10], ['127.0.0.0', 8],
|
|
7
|
-
['169.254.0.0', 16], ['172.16.0.0', 12], ['192.
|
|
7
|
+
['169.254.0.0', 16], ['172.16.0.0', 12], ['192.0.0.0', 24], ['192.0.2.0', 24],
|
|
8
|
+
['192.168.0.0', 16], ['198.18.0.0', 15], ['198.51.100.0', 24],
|
|
9
|
+
['203.0.113.0', 24], ['224.0.0.0', 4], ['240.0.0.0', 4],
|
|
8
10
|
]) blockedAddresses.addSubnet(network, prefix, 'ipv4');
|
|
9
|
-
for (const [network, prefix] of [['::', 128], ['::1', 128], ['fc00::', 7], ['fe80::', 10], ['ff00::', 8]]) {
|
|
11
|
+
for (const [network, prefix] of [['::', 128], ['::1', 128], ['2001:db8::', 32], ['fc00::', 7], ['fe80::', 10], ['ff00::', 8]]) {
|
|
10
12
|
blockedAddresses.addSubnet(network, prefix, 'ipv6');
|
|
11
13
|
}
|
|
12
14
|
|
|
@@ -19,7 +21,7 @@ function isBlockedAddress(address) {
|
|
|
19
21
|
return blockedAddresses.check(address, family === 4 ? 'ipv4' : 'ipv6');
|
|
20
22
|
}
|
|
21
23
|
|
|
22
|
-
async function
|
|
24
|
+
export async function validatePublicUrl(url, { lookup = dns.promises.lookup } = {}) {
|
|
23
25
|
const parsed = new URL(url);
|
|
24
26
|
if (!['http:', 'https:'].includes(parsed.protocol)) throw new Error(`unsupported URL protocol: ${parsed.protocol}`);
|
|
25
27
|
if (parsed.username || parsed.password) throw new Error('URL credentials are not permitted');
|
|
@@ -34,6 +36,32 @@ async function validateDestination(url, lookup) {
|
|
|
34
36
|
return parsed;
|
|
35
37
|
}
|
|
36
38
|
|
|
39
|
+
/** Enforce the public-network policy for each Playwright request. */
|
|
40
|
+
export async function handlePublicBrowserRoute(route, { lookup = dns.promises.lookup } = {}) {
|
|
41
|
+
const url = route.request().url();
|
|
42
|
+
let protocol;
|
|
43
|
+
try {
|
|
44
|
+
protocol = new URL(url).protocol;
|
|
45
|
+
} catch {
|
|
46
|
+
await route.abort('blockedbyclient');
|
|
47
|
+
return;
|
|
48
|
+
}
|
|
49
|
+
if (['data:', 'blob:', 'about:'].includes(protocol)) {
|
|
50
|
+
await route.continue();
|
|
51
|
+
return;
|
|
52
|
+
}
|
|
53
|
+
if (!['http:', 'https:'].includes(protocol)) {
|
|
54
|
+
await route.abort('blockedbyclient');
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
try {
|
|
58
|
+
await validatePublicUrl(url, { lookup });
|
|
59
|
+
await route.continue();
|
|
60
|
+
} catch {
|
|
61
|
+
await route.abort('blockedbyclient');
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
37
65
|
async function readBodyLimited(res, maxBodyBytes) {
|
|
38
66
|
const declared = Number(res.headers.get('content-length'));
|
|
39
67
|
if (Number.isFinite(declared) && declared > maxBodyBytes) {
|
|
@@ -66,13 +94,13 @@ export async function fetchUrl(url, {
|
|
|
66
94
|
maxRetries = 3, retryDelayMs = 1000, maxBodyBytes = 5 * 1024 * 1024,
|
|
67
95
|
fetchImpl = globalThis.fetch, lookup = dns.promises.lookup,
|
|
68
96
|
} = {}) {
|
|
69
|
-
const requested = await
|
|
97
|
+
const requested = await validatePublicUrl(url, { lookup });
|
|
70
98
|
const allowedOrigin = requested.origin;
|
|
71
99
|
const chain = [];
|
|
72
100
|
let current = requested.href;
|
|
73
101
|
|
|
74
102
|
for (let redirects = 0; redirects <= maxRedirects; redirects += 1) {
|
|
75
|
-
await
|
|
103
|
+
await validatePublicUrl(current, { lookup });
|
|
76
104
|
let res;
|
|
77
105
|
let lastError;
|
|
78
106
|
for (let attempt = 0; attempt < Math.max(1, maxRetries); attempt += 1) {
|
|
@@ -108,7 +136,7 @@ export async function fetchUrl(url, {
|
|
|
108
136
|
if (redirects === maxRedirects) break;
|
|
109
137
|
const next = new URL(headers.location, current);
|
|
110
138
|
if (next.origin !== allowedOrigin) throw new Error(`redirect leaves audited origin: ${next.href}`);
|
|
111
|
-
await
|
|
139
|
+
await validatePublicUrl(next.href, { lookup });
|
|
112
140
|
chain.push({ url: current, status: res.status, location: headers.location });
|
|
113
141
|
current = next.href;
|
|
114
142
|
await res.body?.cancel();
|
package/src/detectors/geoReco.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { defineDetector, indexTargets, registryPageFor, sitePageFor, pageSubject, entrySubject } from './framework.js';
|
|
2
|
+
import { classifyAnswerStance } from '../observations/stance.js';
|
|
2
3
|
|
|
3
4
|
const D = [];
|
|
4
5
|
|
|
@@ -223,6 +224,106 @@ D.push(defineDetector({
|
|
|
223
224
|
},
|
|
224
225
|
}));
|
|
225
226
|
|
|
227
|
+
D.push(defineDetector({
|
|
228
|
+
id: 'GEO-007', name: 'Unfavorable or distorted entity stance in recorded generative answers', namespace: 'GEO',
|
|
229
|
+
description: 'Recorded generative engine answers express an unfavorable, negative, or critically distorted stance toward a registered entity.',
|
|
230
|
+
discipline: ['geo', 'aeo'], severity: 'high', deterministic: false, requires: ['registries'],
|
|
231
|
+
finding_type: 'evidence_backed_semantic_finding',
|
|
232
|
+
impact: { representation: 'high', reputational: 'high', conversion: 'medium' },
|
|
233
|
+
applicable_requirement: 'GEO §6: accurate synthesis and entity representation; Rubric: narrative accuracy (§4 & §5)',
|
|
234
|
+
false_positive_conditions: ['Legitimate negative comparison where a product does not support a feature by design'],
|
|
235
|
+
remediation: 'Review the recorded answers against the narrative-accuracy rubric, identify whether the negative characterization reflects true product limitations or factual inaccuracies/outdated information, and publish authoritative corroborating evidence or file model corrections.',
|
|
236
|
+
unsafe_shortcuts: ['publishing fabricated positive reviews', 'manipulating third-party review sites', 'crawler prompt injection (GEO-001)'],
|
|
237
|
+
verification: 'Follow-up generative engine observations demonstrate neutral or favorable stance with no narrative-accuracy violations.',
|
|
238
|
+
review_required: true,
|
|
239
|
+
check(ctx) {
|
|
240
|
+
const entities = (ctx.registries?.entities?.entries || []).filter((e) => e.status !== 'retired');
|
|
241
|
+
if (!entities.length) return [];
|
|
242
|
+
const hits = [];
|
|
243
|
+
|
|
244
|
+
// 1. Check promptResults
|
|
245
|
+
for (const pr of ctx.promptResults || []) {
|
|
246
|
+
for (const e of entities) {
|
|
247
|
+
const names = [e.canonical_name, ...(e.aliases || [])].map((n) => n.toLowerCase());
|
|
248
|
+
const text = `${pr.prompt_text || ''} ${pr.answer_text || ''}`.toLowerCase();
|
|
249
|
+
const mentioned = names.some((n) => text.includes(n));
|
|
250
|
+
if (!mentioned) continue;
|
|
251
|
+
|
|
252
|
+
const classification = classifyAnswerStance(pr.answer_text || '', e, { reviewer: pr.evaluator });
|
|
253
|
+
const isNegativeSentiment = pr.sentiment === 'negative' || pr.recommendation_status === 'not_recommended';
|
|
254
|
+
if (classification.stance === 'unfavorable' || isNegativeSentiment) {
|
|
255
|
+
hits.push({
|
|
256
|
+
subject: entrySubject('entities', e.entity_id),
|
|
257
|
+
summary: `Generative engine ${pr.engine || 'unknown'} expresses unfavorable stance toward entity "${e.canonical_name}"`,
|
|
258
|
+
evidence: [
|
|
259
|
+
`engine: ${pr.engine || 'unknown'}`,
|
|
260
|
+
`prompt: ${pr.prompt_text || pr.prompt_id || 'unknown'}`,
|
|
261
|
+
`stance: unfavorable`,
|
|
262
|
+
`markers: ${classification.unfavorable_markers?.join(', ') || 'recorded negative sentiment'}`,
|
|
263
|
+
`excerpt: "${(classification.evidence_passages?.[0] || pr.answer_text || '').slice(0, 160)}"`,
|
|
264
|
+
`reviewer: ${pr.evaluator || 'unreviewed (semantic review required)'}`,
|
|
265
|
+
],
|
|
266
|
+
confidence: pr.evaluator ? 'confirmed' : 'medium',
|
|
267
|
+
finding_type: 'evidence_backed_semantic_finding',
|
|
268
|
+
});
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
// 2. Check observations (stance or citation)
|
|
274
|
+
for (const obs of ctx.observations || []) {
|
|
275
|
+
if (obs.kind === 'stance' && obs.data?.stance === 'unfavorable') {
|
|
276
|
+
const e = entities.find((ent) => ent.entity_id === obs.data.entity_id);
|
|
277
|
+
if (e) {
|
|
278
|
+
hits.push({
|
|
279
|
+
subject: entrySubject('entities', e.entity_id),
|
|
280
|
+
summary: `Generative engine ${obs.data.engine || 'unknown'} expresses unfavorable stance toward entity "${e.canonical_name}"`,
|
|
281
|
+
evidence: [
|
|
282
|
+
`engine: ${obs.data.engine || 'unknown'}`,
|
|
283
|
+
`prompt: ${obs.data.prompt_text || obs.data.prompt_id || 'unknown'}`,
|
|
284
|
+
`stance: unfavorable`,
|
|
285
|
+
`markers: ${(obs.data.unfavorable_markers || []).join(', ') || 'unfavorable markers'}`,
|
|
286
|
+
`excerpt: "${(obs.data.evidence_passages?.[0] || '').slice(0, 160)}"`,
|
|
287
|
+
`reviewer: ${obs.data.reviewer || 'unreviewed (semantic review required)'}`,
|
|
288
|
+
],
|
|
289
|
+
confidence: obs.data.reviewer ? 'confirmed' : 'medium',
|
|
290
|
+
finding_type: 'evidence_backed_semantic_finding',
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
} else if (obs.kind === 'citation' && obs.data?.answer_text) {
|
|
294
|
+
for (const e of entities) {
|
|
295
|
+
const classification = classifyAnswerStance(obs.data.answer_text, e);
|
|
296
|
+
if (classification.mentioned && classification.stance === 'unfavorable') {
|
|
297
|
+
hits.push({
|
|
298
|
+
subject: entrySubject('entities', e.entity_id),
|
|
299
|
+
summary: `Generative engine ${obs.data.provider || 'unknown'} expresses unfavorable stance toward entity "${e.canonical_name}"`,
|
|
300
|
+
evidence: [
|
|
301
|
+
`engine: ${obs.data.provider || 'unknown'}`,
|
|
302
|
+
`prompt: ${obs.data.prompt_text || obs.data.prompt_id || 'unknown'}`,
|
|
303
|
+
`stance: unfavorable`,
|
|
304
|
+
`markers: ${(classification.unfavorable_markers || []).join(', ')}`,
|
|
305
|
+
`excerpt: "${(classification.evidence_passages?.[0] || obs.data.answer_text).slice(0, 160)}"`,
|
|
306
|
+
`reviewer: unreviewed (semantic review required)`,
|
|
307
|
+
],
|
|
308
|
+
confidence: 'medium',
|
|
309
|
+
finding_type: 'evidence_backed_semantic_finding',
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
// Deduplicate by entity and summary
|
|
317
|
+
const seen = new Set();
|
|
318
|
+
return hits.filter((h) => {
|
|
319
|
+
const k = `${h.subject.identifier}|${h.summary}`;
|
|
320
|
+
if (seen.has(k)) return false;
|
|
321
|
+
seen.add(k);
|
|
322
|
+
return true;
|
|
323
|
+
});
|
|
324
|
+
},
|
|
325
|
+
}));
|
|
326
|
+
|
|
226
327
|
/* ---------------- RECO: recommendation eligibility ---------------- */
|
|
227
328
|
|
|
228
329
|
const RECO_CHECKS = [
|
package/src/extractor/page.js
CHANGED
|
@@ -57,6 +57,21 @@ export function extractPage({ url, html, status = 200, headers = {}, sourceFile
|
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
const body = root.querySelector('body');
|
|
60
|
+
const rawVisibleText = body ? body.text.replace(/\s+/g, ' ').trim() : '';
|
|
61
|
+
const structuralRegions = [];
|
|
62
|
+
const seenRegions = new Set();
|
|
63
|
+
for (const region of root.querySelectorAll('header,nav,aside,footer,[role=banner],[role=navigation],[role=complementary],[role=contentinfo]')) {
|
|
64
|
+
if (seenRegions.has(region)) continue;
|
|
65
|
+
seenRegions.add(region);
|
|
66
|
+
const text = region.text.replace(/\s+/g, ' ').trim();
|
|
67
|
+
if (!text) continue;
|
|
68
|
+
structuralRegions.push({
|
|
69
|
+
region_type: (region.getAttribute('role') || region.tagName || 'unknown').toLowerCase(),
|
|
70
|
+
text,
|
|
71
|
+
html_bytes: Buffer.byteLength(region.outerHTML),
|
|
72
|
+
token_count: text.split(/\s+/).filter(Boolean).length,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
60
75
|
// Strip script/style/nav/footer noise for main-text estimation
|
|
61
76
|
const bodyClone = body ? parse(body.outerHTML) : null;
|
|
62
77
|
if (bodyClone) for (const s of bodyClone.querySelectorAll('script,style,noscript,nav,footer,header[role=banner]')) s.remove();
|
|
@@ -115,6 +130,8 @@ export function extractPage({ url, html, status = 200, headers = {}, sourceFile
|
|
|
115
130
|
images,
|
|
116
131
|
jsonLd,
|
|
117
132
|
text,
|
|
133
|
+
rawVisibleWordCount: rawVisibleText ? rawVisibleText.split(/\s+/).length : 0,
|
|
134
|
+
structuralRegions,
|
|
118
135
|
paragraphs,
|
|
119
136
|
wordCount: text ? text.split(/\s+/).length : 0,
|
|
120
137
|
scriptBytes,
|
package/src/installer/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import {
|
|
|
8
8
|
PROVIDERS,
|
|
9
9
|
PROVIDER_IDS,
|
|
10
10
|
parseProviderList,
|
|
11
|
+
providerAgentProfilesDestination,
|
|
11
12
|
providerBundleSkillPath,
|
|
12
13
|
providerDestination,
|
|
13
14
|
providerSkillsDir,
|
|
@@ -421,7 +422,7 @@ function toPosix(value) {
|
|
|
421
422
|
|
|
422
423
|
function makeInstalledManifest(target, packageInfo, tree) {
|
|
423
424
|
return {
|
|
424
|
-
name: 'citable',
|
|
425
|
+
name: target.manifestName || 'citable',
|
|
425
426
|
version: packageInfo.version,
|
|
426
427
|
managedBy: 'citable-cli',
|
|
427
428
|
provider: target.providerId,
|
|
@@ -471,7 +472,7 @@ export function inspectInstallation(target, options = {}) {
|
|
|
471
472
|
} catch (err) {
|
|
472
473
|
return { state: 'corrupt', installed: null, path: destination, problems: [`manifest parse failed: ${err.message}`], symlink, localModifications: 'unknown' };
|
|
473
474
|
}
|
|
474
|
-
if (manifest.managedBy !== 'citable-cli' || manifest.name !== 'citable') {
|
|
475
|
+
if (manifest.managedBy !== 'citable-cli' || manifest.name !== (target.manifestName || 'citable')) {
|
|
475
476
|
return { state: 'unmanaged', installed: manifest.version ?? null, path: destination, problems: ['manifest is not managed by citable-cli'], manifest, symlink, localModifications: 'unknown' };
|
|
476
477
|
}
|
|
477
478
|
|
|
@@ -543,6 +544,13 @@ export function createInstallPlan(command, args, options = {}) {
|
|
|
543
544
|
}
|
|
544
545
|
const status = inspectInstallation(target, { availableTree: bundle.tree, availableVersion: packageInfo.version });
|
|
545
546
|
const action = plannedAction(command, status, args);
|
|
547
|
+
let profiles = null;
|
|
548
|
+
try {
|
|
549
|
+
profiles = prepareAgentProfiles(target, bundle, packageInfo, args, command, options);
|
|
550
|
+
} catch (err) {
|
|
551
|
+
results.push(targetResult(target, 'failed', err.message, { exitCode: err.exitCode ?? EXIT_CODES.integrityFailure }));
|
|
552
|
+
continue;
|
|
553
|
+
}
|
|
546
554
|
results.push({
|
|
547
555
|
provider: target.providerId,
|
|
548
556
|
providerName: target.provider.displayName,
|
|
@@ -558,6 +566,16 @@ export function createInstallPlan(command, args, options = {}) {
|
|
|
558
566
|
filesToRemove: [],
|
|
559
567
|
collisions: collisionProblems(status, args),
|
|
560
568
|
localModifications: status.localModifications,
|
|
569
|
+
agentProfiles: profiles ? {
|
|
570
|
+
path: profiles.target.destination,
|
|
571
|
+
state: profiles.before.state,
|
|
572
|
+
action: profiles.action,
|
|
573
|
+
filesToCreate: ['install', 'replace', 'update', 'repair'].includes(profiles.action)
|
|
574
|
+
? Object.keys(profiles.tree.files)
|
|
575
|
+
: [],
|
|
576
|
+
collisions: collisionProblems(profiles.before, args),
|
|
577
|
+
localModifications: profiles.before.localModifications,
|
|
578
|
+
} : null,
|
|
561
579
|
validation: ['validate package bundle', 'validate provider target', 'validate staged skill', 'validate final installation'],
|
|
562
580
|
});
|
|
563
581
|
}
|
|
@@ -674,41 +692,65 @@ function inspectInstallationWithBundle(target, options = {}) {
|
|
|
674
692
|
function installOrUpdateTarget(target, args, options = {}, command = 'install') {
|
|
675
693
|
let stage = null;
|
|
676
694
|
let backup = null;
|
|
695
|
+
let installedSkill = false;
|
|
677
696
|
try {
|
|
678
697
|
const packageInfo = loadPackageInfo(options.packageRoot || packageRoot());
|
|
679
698
|
const bundle = validateProviderBundle(target.providerId, options);
|
|
680
699
|
const before = inspectInstallation(target, { availableTree: bundle.tree, availableVersion: packageInfo.version });
|
|
681
700
|
const action = plannedAction(command, before, args);
|
|
682
|
-
|
|
701
|
+
const profiles = prepareAgentProfiles(target, bundle, packageInfo, args, command, options);
|
|
702
|
+
if (action === 'already current' && (!profiles || profiles.action === 'already current')) {
|
|
703
|
+
return targetResult(target, 'already current', 'already current', { before, agentProfiles: profiles?.before ?? null });
|
|
704
|
+
}
|
|
683
705
|
if (action === 'not installed') return targetResult(target, 'skipped', 'not installed', { before });
|
|
684
706
|
if (action === 'refuse') {
|
|
685
707
|
const code = before.state === 'unmanaged' ? EXIT_CODES.installationCollision : EXIT_CODES.integrityFailure;
|
|
686
708
|
return targetResult(target, 'failed', `${before.state}: ${before.problems.join('; ') || 'refusing to overwrite without --force'}`, { before, exitCode: code });
|
|
687
709
|
}
|
|
710
|
+
if (profiles?.action === 'refuse') {
|
|
711
|
+
const code = profiles.before.state === 'unmanaged' ? EXIT_CODES.installationCollision : EXIT_CODES.integrityFailure;
|
|
712
|
+
return targetResult(target, 'failed', `agent profile ${profiles.before.state}: ${profiles.before.problems.join('; ') || 'refusing to overwrite without --force'}`, {
|
|
713
|
+
before,
|
|
714
|
+
agentProfiles: profiles.before,
|
|
715
|
+
exitCode: code,
|
|
716
|
+
});
|
|
717
|
+
}
|
|
688
718
|
|
|
689
719
|
const installPath = target.realDestination || target.destination;
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
720
|
+
let after = before;
|
|
721
|
+
if (action !== 'already current') {
|
|
722
|
+
const parent = path.dirname(installPath);
|
|
723
|
+
fs.mkdirSync(parent, { recursive: true });
|
|
724
|
+
ensureTargetInsideScope(parent, target.scopeRoot);
|
|
725
|
+
stage = path.join(parent, `.citable-install-${process.pid}-${Date.now()}-${target.providerId}`);
|
|
726
|
+
backup = path.join(parent, `.citable-backup-${Date.now()}-${target.providerId}`);
|
|
727
|
+
copyDirSafe(bundle.source, stage);
|
|
728
|
+
const stagedTree = hashTree(stage, { exclude: ['manifest.json'] });
|
|
729
|
+
writeJsonFile(path.join(stage, 'manifest.json'), makeInstalledManifest(target, packageInfo, stagedTree));
|
|
730
|
+
validateStagedSkill(stage, target.providerId);
|
|
731
|
+
|
|
732
|
+
if (exists(installPath)) fs.renameSync(installPath, backup);
|
|
733
|
+
fs.renameSync(stage, installPath);
|
|
734
|
+
stage = null;
|
|
735
|
+
installedSkill = true;
|
|
736
|
+
|
|
737
|
+
after = inspectInstallation(target, { availableTree: stagedTree, availableVersion: packageInfo.version });
|
|
738
|
+
if (after.state !== 'current') {
|
|
739
|
+
throw new CitableInstallerError(`final validation failed: ${after.state}`, EXIT_CODES.integrityFailure, { after });
|
|
740
|
+
}
|
|
707
741
|
}
|
|
708
|
-
|
|
742
|
+
const profileResult = profiles ? installAgentProfiles(profiles, packageInfo) : null;
|
|
743
|
+
const resultStatus = action === 'update' || (action === 'already current' && profileResult?.changed) ? 'updated' : 'installed';
|
|
744
|
+
return targetResult(target, resultStatus, `${action === 'already current' ? 'agent profile install' : action} complete`, {
|
|
745
|
+
before,
|
|
746
|
+
after,
|
|
747
|
+
agentProfiles: profileResult,
|
|
748
|
+
backup: exists(backup) ? backup : null,
|
|
749
|
+
});
|
|
709
750
|
} catch (err) {
|
|
710
751
|
if (stage) safeRm(stage);
|
|
711
752
|
const installPath = target.realDestination || target.destination;
|
|
753
|
+
if (installedSkill && exists(installPath)) safeRm(installPath);
|
|
712
754
|
if (backup && exists(backup) && !exists(installPath)) {
|
|
713
755
|
try {
|
|
714
756
|
fs.renameSync(backup, installPath);
|
|
@@ -720,6 +762,80 @@ function installOrUpdateTarget(target, args, options = {}, command = 'install')
|
|
|
720
762
|
}
|
|
721
763
|
}
|
|
722
764
|
|
|
765
|
+
function prepareAgentProfiles(target, bundle, packageInfo, args, command, options) {
|
|
766
|
+
const metadata = bundle.manifest.agentProfiles;
|
|
767
|
+
if (!metadata || metadata.status !== 'available') return null;
|
|
768
|
+
const destination = providerAgentProfilesDestination(target.providerId, target.scope, rootsFor(options));
|
|
769
|
+
if (!destination) throw new CitableInstallerError(`agent profiles are not supported for ${target.provider.displayName}`, EXIT_CODES.unsupportedEnvironment);
|
|
770
|
+
const source = path.resolve(bundleRoot(options.packageRoot || packageRoot()), metadata.path);
|
|
771
|
+
const sourceManifest = readJson(path.join(source, 'manifest.json'), `${target.providerId} agent profile manifest`);
|
|
772
|
+
if (
|
|
773
|
+
sourceManifest.name !== 'citable-agent-profiles'
|
|
774
|
+
|| sourceManifest.provider !== target.providerId
|
|
775
|
+
|| sourceManifest.managedBy !== 'citable-cli'
|
|
776
|
+
) {
|
|
777
|
+
throw new CitableInstallerError(`agent profile manifest for ${target.provider.displayName} is invalid`, EXIT_CODES.integrityFailure);
|
|
778
|
+
}
|
|
779
|
+
const tree = hashTree(source, { exclude: ['manifest.json'] });
|
|
780
|
+
if (tree.treeHash !== metadata.treeHash || tree.treeHash !== sourceManifest.treeHash) {
|
|
781
|
+
throw new CitableInstallerError(`agent profile bundle hash mismatch for ${target.provider.displayName}`, EXIT_CODES.integrityFailure);
|
|
782
|
+
}
|
|
783
|
+
const profileTarget = {
|
|
784
|
+
...target,
|
|
785
|
+
destination,
|
|
786
|
+
realDestination: null,
|
|
787
|
+
manifestName: 'citable-agent-profiles',
|
|
788
|
+
};
|
|
789
|
+
const before = inspectInstallation(profileTarget, { availableTree: tree, availableVersion: packageInfo.version });
|
|
790
|
+
return {
|
|
791
|
+
target: profileTarget,
|
|
792
|
+
source,
|
|
793
|
+
tree,
|
|
794
|
+
before,
|
|
795
|
+
action: plannedAction(command, before, args),
|
|
796
|
+
};
|
|
797
|
+
}
|
|
798
|
+
|
|
799
|
+
function installAgentProfiles(profiles, packageInfo) {
|
|
800
|
+
if (profiles.action === 'already current') return { changed: false, before: profiles.before, after: profiles.before };
|
|
801
|
+
const destination = profiles.target.destination;
|
|
802
|
+
const parent = path.dirname(destination);
|
|
803
|
+
let stage = null;
|
|
804
|
+
let backup = null;
|
|
805
|
+
try {
|
|
806
|
+
fs.mkdirSync(parent, { recursive: true });
|
|
807
|
+
ensureTargetInsideScope(parent, profiles.target.scopeRoot);
|
|
808
|
+
stage = path.join(parent, `.citable-agent-install-${process.pid}-${Date.now()}`);
|
|
809
|
+
backup = path.join(parent, `.citable-agent-backup-${Date.now()}`);
|
|
810
|
+
copyDirSafe(profiles.source, stage);
|
|
811
|
+
writeJsonFile(path.join(stage, 'manifest.json'), makeInstalledManifest(profiles.target, packageInfo, profiles.tree));
|
|
812
|
+
validateStagedProfiles(stage, profiles.target.providerId, profiles.tree);
|
|
813
|
+
if (exists(destination)) fs.renameSync(destination, backup);
|
|
814
|
+
fs.renameSync(stage, destination);
|
|
815
|
+
stage = null;
|
|
816
|
+
const after = inspectInstallation(profiles.target, { availableTree: profiles.tree, availableVersion: packageInfo.version });
|
|
817
|
+
if (after.state !== 'current') throw new CitableInstallerError(`final agent profile validation failed: ${after.state}`, EXIT_CODES.integrityFailure);
|
|
818
|
+
return { changed: true, before: profiles.before, after, backup: exists(backup) ? backup : null };
|
|
819
|
+
} catch (err) {
|
|
820
|
+
if (stage) safeRm(stage);
|
|
821
|
+
if (exists(destination)) safeRm(destination);
|
|
822
|
+
if (backup && exists(backup)) fs.renameSync(backup, destination);
|
|
823
|
+
throw err;
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
|
|
827
|
+
function validateStagedProfiles(dir, providerId, tree) {
|
|
828
|
+
for (const rel of Object.keys(tree.files)) {
|
|
829
|
+
if (!exists(path.join(dir, rel))) {
|
|
830
|
+
throw new CitableInstallerError(`staged ${providerId} agent profiles are missing ${rel}`, EXIT_CODES.integrityFailure);
|
|
831
|
+
}
|
|
832
|
+
}
|
|
833
|
+
const manifest = readJson(path.join(dir, 'manifest.json'), 'staged agent profile manifest');
|
|
834
|
+
if (manifest.managedBy !== 'citable-cli' || manifest.provider !== providerId || manifest.name !== 'citable-agent-profiles') {
|
|
835
|
+
throw new CitableInstallerError(`staged ${providerId} agent profile manifest is invalid`, EXIT_CODES.integrityFailure);
|
|
836
|
+
}
|
|
837
|
+
}
|
|
838
|
+
|
|
723
839
|
function ensureTargetInsideScope(targetPath, scopeRoot) {
|
|
724
840
|
// Use realpathSync when possible to resolve symlinks (e.g. /tmp → /private/tmp on macOS)
|
|
725
841
|
// so that cross-symlink paths compare correctly.
|
|
@@ -777,6 +893,7 @@ function uninstallTarget(target, args) {
|
|
|
777
893
|
if (status.manifest?.managedBy !== 'citable-cli') {
|
|
778
894
|
return targetResult(target, 'failed', `${status.state}: refusing to remove unmanaged content`, { before: status, exitCode: EXIT_CODES.installationCollision });
|
|
779
895
|
}
|
|
896
|
+
const profileResult = uninstallAgentProfiles(target);
|
|
780
897
|
const destination = target.realDestination || target.destination;
|
|
781
898
|
const relFiles = Object.keys(status.manifest.files || {}).sort((a, b) => b.localeCompare(a));
|
|
782
899
|
for (const rel of relFiles) {
|
|
@@ -790,12 +907,39 @@ function uninstallTarget(target, args) {
|
|
|
790
907
|
} catch {
|
|
791
908
|
// Directory contains user files; preserve it.
|
|
792
909
|
}
|
|
793
|
-
return targetResult(target, 'removed', 'removed managed files', {
|
|
910
|
+
return targetResult(target, 'removed', 'removed managed files', {
|
|
911
|
+
before: status,
|
|
912
|
+
agentProfiles: profileResult,
|
|
913
|
+
removedFiles: [...relFiles, 'manifest.json'],
|
|
914
|
+
});
|
|
794
915
|
} catch (err) {
|
|
795
916
|
return targetResult(target, 'failed', err.message, { exitCode: err.exitCode ?? EXIT_CODES.generalFailure });
|
|
796
917
|
}
|
|
797
918
|
}
|
|
798
919
|
|
|
920
|
+
function uninstallAgentProfiles(target) {
|
|
921
|
+
const destination = providerAgentProfilesDestination(target.providerId, target.scope, {
|
|
922
|
+
projectRoot: target.scope === 'project' ? target.scopeRoot : '',
|
|
923
|
+
home: target.scope === 'global' ? target.scopeRoot : '',
|
|
924
|
+
});
|
|
925
|
+
if (!destination) return null;
|
|
926
|
+
const profileTarget = { ...target, destination, realDestination: null, manifestName: 'citable-agent-profiles' };
|
|
927
|
+
const status = inspectInstallation(profileTarget);
|
|
928
|
+
if (status.state === 'not installed') return { status: 'not installed' };
|
|
929
|
+
if (status.manifest?.managedBy !== 'citable-cli' || status.manifest?.name !== 'citable-agent-profiles') {
|
|
930
|
+
return { status: 'preserved', state: status.state, problems: status.problems };
|
|
931
|
+
}
|
|
932
|
+
const relFiles = Object.keys(status.manifest.files || {}).sort((a, b) => b.localeCompare(a));
|
|
933
|
+
for (const rel of relFiles) {
|
|
934
|
+
assertArchiveEntrySafe(rel);
|
|
935
|
+
safeUnlink(path.join(destination, rel));
|
|
936
|
+
}
|
|
937
|
+
safeUnlink(path.join(destination, 'manifest.json'));
|
|
938
|
+
pruneEmptyDirs(destination, destination);
|
|
939
|
+
if (exists(destination) && fs.readdirSync(destination).length === 0) fs.rmdirSync(destination);
|
|
940
|
+
return { status: 'removed', removedFiles: [...relFiles, 'manifest.json'] };
|
|
941
|
+
}
|
|
942
|
+
|
|
799
943
|
function safeUnlink(filePath) {
|
|
800
944
|
try {
|
|
801
945
|
fs.unlinkSync(filePath);
|
|
@@ -841,13 +985,31 @@ async function inspectCommand(command, args, options = {}) {
|
|
|
841
985
|
const packageInfo = loadPackageInfo(context.roots.packageRoot);
|
|
842
986
|
const rows = [];
|
|
843
987
|
for (const target of context.targets.filter((target) => !target.duplicateOf)) {
|
|
988
|
+
let bundle = null;
|
|
844
989
|
let bundleTree = null;
|
|
845
990
|
try {
|
|
846
|
-
|
|
991
|
+
bundle = validateProviderBundle(target.providerId, { ...options, packageRoot: context.roots.packageRoot });
|
|
992
|
+
bundleTree = bundle.tree;
|
|
847
993
|
} catch {
|
|
848
994
|
// Check/list should still report installed state if the package bundle is broken.
|
|
849
995
|
}
|
|
850
996
|
const status = inspectInstallation(target, { availableTree: bundleTree, availableVersion: packageInfo.version });
|
|
997
|
+
let agentProfiles = null;
|
|
998
|
+
if (bundle) {
|
|
999
|
+
try {
|
|
1000
|
+
agentProfiles = prepareAgentProfiles(target, bundle, packageInfo, args, 'install', options)?.before ?? null;
|
|
1001
|
+
} catch (err) {
|
|
1002
|
+
agentProfiles = {
|
|
1003
|
+
state: 'corrupt',
|
|
1004
|
+
installed: null,
|
|
1005
|
+
path: providerAgentProfilesDestination(target.providerId, target.scope, context.roots),
|
|
1006
|
+
localModifications: 'unknown',
|
|
1007
|
+
problems: [err.message],
|
|
1008
|
+
};
|
|
1009
|
+
}
|
|
1010
|
+
}
|
|
1011
|
+
const combinedState = combineInstalledState(status.state, agentProfiles?.state);
|
|
1012
|
+
const profileProblems = (agentProfiles?.problems || []).map((problem) => `agent profiles: ${problem}`);
|
|
851
1013
|
rows.push({
|
|
852
1014
|
provider: target.providerId,
|
|
853
1015
|
providerName: target.provider.displayName,
|
|
@@ -855,9 +1017,10 @@ async function inspectCommand(command, args, options = {}) {
|
|
|
855
1017
|
path: target.destination,
|
|
856
1018
|
installed: status.installed,
|
|
857
1019
|
available: packageInfo.version,
|
|
858
|
-
state:
|
|
859
|
-
localModifications: status.localModifications,
|
|
860
|
-
problems: status.problems,
|
|
1020
|
+
state: combinedState,
|
|
1021
|
+
localModifications: status.localModifications === 'present' || agentProfiles?.localModifications === 'present' ? 'present' : status.localModifications,
|
|
1022
|
+
problems: [...status.problems, ...profileProblems],
|
|
1023
|
+
agentProfiles,
|
|
861
1024
|
symlink: Boolean(status.symlink),
|
|
862
1025
|
});
|
|
863
1026
|
}
|
|
@@ -865,9 +1028,18 @@ async function inspectCommand(command, args, options = {}) {
|
|
|
865
1028
|
return { ok: true, command, providers: rows, exitCode: args.failOnUpdate && hasUpdate ? EXIT_CODES.updateAvailable : EXIT_CODES.success };
|
|
866
1029
|
}
|
|
867
1030
|
|
|
1031
|
+
function combineInstalledState(skillState, profileState) {
|
|
1032
|
+
if (!profileState || profileState === 'current') return skillState;
|
|
1033
|
+
if (skillState !== 'current') return skillState;
|
|
1034
|
+
if (profileState === 'not installed') return 'partial';
|
|
1035
|
+
if (profileState === 'update available') return 'update available';
|
|
1036
|
+
return profileState;
|
|
1037
|
+
}
|
|
1038
|
+
|
|
868
1039
|
export async function doctorCommand(args, options = {}) {
|
|
869
1040
|
const context = resolveTargets('doctor', args, options);
|
|
870
1041
|
const packageInfo = loadPackageInfo(context.roots.packageRoot);
|
|
1042
|
+
const capabilities = await runtimeCapabilities(options);
|
|
871
1043
|
const checks = [];
|
|
872
1044
|
const nodeMajor = Number.parseInt(process.versions.node.split('.')[0], 10);
|
|
873
1045
|
checks.push({ level: nodeMajor >= 20 ? 'PASS' : 'FAIL', message: `Node.js ${process.versions.node}` });
|
|
@@ -902,7 +1074,81 @@ export async function doctorCommand(args, options = {}) {
|
|
|
902
1074
|
}
|
|
903
1075
|
|
|
904
1076
|
const hasFail = checks.some((check) => check.level === 'FAIL');
|
|
905
|
-
return { ok: !hasFail, command: 'doctor', checks, exitCode: hasFail ? EXIT_CODES.integrityFailure : EXIT_CODES.success };
|
|
1077
|
+
return { ok: !hasFail, command: 'doctor', checks, capabilities, exitCode: hasFail ? EXIT_CODES.integrityFailure : EXIT_CODES.success };
|
|
1078
|
+
}
|
|
1079
|
+
|
|
1080
|
+
async function optionalDependencyAvailable(name, options = {}) {
|
|
1081
|
+
if (options.capabilityResolver) return Boolean(await options.capabilityResolver(name));
|
|
1082
|
+
try {
|
|
1083
|
+
await import(name);
|
|
1084
|
+
return true;
|
|
1085
|
+
} catch {
|
|
1086
|
+
return false;
|
|
1087
|
+
}
|
|
1088
|
+
}
|
|
1089
|
+
|
|
1090
|
+
export async function runtimeCapabilities(options = {}) {
|
|
1091
|
+
const env = options.env || process.env;
|
|
1092
|
+
const dependencyDefinitions = [
|
|
1093
|
+
{
|
|
1094
|
+
id: 'browser_render',
|
|
1095
|
+
requirements: ['playwright'],
|
|
1096
|
+
readyLimitations: ['dependency presence does not establish browser launch readiness'],
|
|
1097
|
+
},
|
|
1098
|
+
{
|
|
1099
|
+
id: 'lighthouse_lab',
|
|
1100
|
+
requirements: ['lighthouse', 'chrome-launcher'],
|
|
1101
|
+
readyLimitations: ['dependency presence does not establish Chrome launch readiness or a successful lab run'],
|
|
1102
|
+
},
|
|
1103
|
+
{
|
|
1104
|
+
id: 'ocr',
|
|
1105
|
+
requirements: ['tesseract.js'],
|
|
1106
|
+
readyLimitations: ['dependency presence does not establish OCR accuracy or source-image availability'],
|
|
1107
|
+
},
|
|
1108
|
+
];
|
|
1109
|
+
const capabilities = [];
|
|
1110
|
+
for (const definition of dependencyDefinitions) {
|
|
1111
|
+
const availability = await Promise.all(definition.requirements.map(async (name) => ({
|
|
1112
|
+
name,
|
|
1113
|
+
available: await optionalDependencyAvailable(name, options),
|
|
1114
|
+
})));
|
|
1115
|
+
const missing = availability.filter((item) => !item.available).map((item) => item.name);
|
|
1116
|
+
capabilities.push({
|
|
1117
|
+
id: definition.id,
|
|
1118
|
+
state: missing.length ? 'missing_dependency' : 'ready',
|
|
1119
|
+
requirements: definition.requirements,
|
|
1120
|
+
limitations: missing.length
|
|
1121
|
+
? missing.map((name) => `optional dependency ${name} is not installed`)
|
|
1122
|
+
: definition.readyLimitations,
|
|
1123
|
+
});
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
for (const definition of [
|
|
1127
|
+
{
|
|
1128
|
+
id: 'crux_field',
|
|
1129
|
+
credential: 'CRUX_API_KEY',
|
|
1130
|
+
readyLimitation: 'credential presence does not establish validity, scope, quota, or API availability',
|
|
1131
|
+
},
|
|
1132
|
+
{
|
|
1133
|
+
id: 'gsc_index',
|
|
1134
|
+
credential: 'GSC_ACCESS_TOKEN',
|
|
1135
|
+
readyLimitation: 'credential presence does not establish validity, scope, property access, or API availability',
|
|
1136
|
+
},
|
|
1137
|
+
{
|
|
1138
|
+
id: 'ga4_metrics',
|
|
1139
|
+
credential: 'GA4_ACCESS_TOKEN',
|
|
1140
|
+
readyLimitation: 'credential presence does not establish validity, scope, property access, or API availability',
|
|
1141
|
+
},
|
|
1142
|
+
]) {
|
|
1143
|
+
const configured = typeof env[definition.credential] === 'string' && env[definition.credential].length > 0;
|
|
1144
|
+
capabilities.push({
|
|
1145
|
+
id: definition.id,
|
|
1146
|
+
state: configured ? 'ready' : 'missing_credential',
|
|
1147
|
+
requirements: [definition.credential],
|
|
1148
|
+
limitations: [configured ? definition.readyLimitation : `${definition.credential} is not configured`],
|
|
1149
|
+
});
|
|
1150
|
+
}
|
|
1151
|
+
return capabilities;
|
|
906
1152
|
}
|
|
907
1153
|
|
|
908
1154
|
function inspectInstallationWithOptionalBundle(target, packageRootPath, version) {
|
|
@@ -1103,7 +1349,15 @@ function renderList(result) {
|
|
|
1103
1349
|
}
|
|
1104
1350
|
|
|
1105
1351
|
function renderDoctor(result) {
|
|
1106
|
-
|
|
1352
|
+
const lines = ['Citable doctor', '', ...result.checks.map((check) => `${check.level.padEnd(5)} ${check.message}`)];
|
|
1353
|
+
if (result.capabilities?.length) {
|
|
1354
|
+
lines.push('', 'Runtime capabilities');
|
|
1355
|
+
for (const capability of result.capabilities) {
|
|
1356
|
+
lines.push(` ${capability.id.padEnd(18)} ${capability.state}`);
|
|
1357
|
+
for (const limitation of capability.limitations) lines.push(` ${limitation}`);
|
|
1358
|
+
}
|
|
1359
|
+
}
|
|
1360
|
+
return lines.join('\n');
|
|
1107
1361
|
}
|
|
1108
1362
|
|
|
1109
1363
|
export function installerHelp() {
|