@azure-id/orc 0.55.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 +2017 -0
- package/README-id.md +521 -0
- package/README.md +485 -0
- package/bin/build-agents.js +86 -0
- package/bin/cli.js +29050 -0
- package/bin/mockrun-catalog.js +267 -0
- package/bin/onboarding-content.js +172 -0
- package/bin/pricing.json +200 -0
- package/bin/providers.json +437 -0
- package/bin/ui.js +95 -0
- package/bin/verify-contracts.js +3277 -0
- package/bin/verify-package.js +498 -0
- package/bin/webui/api.js +1163 -0
- package/bin/webui/app.html +198 -0
- package/bin/webui/css/00-tokens.css +92 -0
- package/bin/webui/css/01-base.css +42 -0
- package/bin/webui/css/02-shell.css +103 -0
- package/bin/webui/css/03-components.css +462 -0
- package/bin/webui/css/04-motion.css +91 -0
- package/bin/webui/css/05-tour.css +100 -0
- package/bin/webui/css/06-responsive.css +108 -0
- package/bin/webui/css/panels/boundary.css +26 -0
- package/bin/webui/css/panels/challenge.css +151 -0
- package/bin/webui/css/panels/crosslink.css +194 -0
- package/bin/webui/css/panels/docs.css +254 -0
- package/bin/webui/css/panels/experiment.css +23 -0
- package/bin/webui/css/panels/extra.css +536 -0
- package/bin/webui/css/panels/flow.css +177 -0
- package/bin/webui/css/panels/handoff.css +67 -0
- package/bin/webui/css/panels/knowledge.css +86 -0
- package/bin/webui/css/panels/learn.css +154 -0
- package/bin/webui/css/panels/maintenance.css +55 -0
- package/bin/webui/css/panels/mockrun.css +268 -0
- package/bin/webui/css/panels/overview.css +100 -0
- package/bin/webui/css/panels/pact.css +43 -0
- package/bin/webui/css/panels/runs.css +105 -0
- package/bin/webui/css/panels/settings.css +227 -0
- package/bin/webui/css/panels/stats.css +87 -0
- package/bin/webui/fixtures/boundary.js +92 -0
- package/bin/webui/fixtures/challenge.js +825 -0
- package/bin/webui/fixtures/crosslink.js +31 -0
- package/bin/webui/fixtures/docs.js +982 -0
- package/bin/webui/fixtures/extra.js +1934 -0
- package/bin/webui/fixtures/flow.js +81 -0
- package/bin/webui/fixtures/handoff.js +35 -0
- package/bin/webui/fixtures/index.js +486 -0
- package/bin/webui/fixtures/knowledge.js +291 -0
- package/bin/webui/fixtures/maintenance.js +42 -0
- package/bin/webui/fixtures/mockrun.js +36 -0
- package/bin/webui/fixtures/pact.js +111 -0
- package/bin/webui/fixtures/runs.js +109 -0
- package/bin/webui/fixtures/settings.js +78 -0
- package/bin/webui/fixtures/shell.js +48 -0
- package/bin/webui/fixtures/stats.js +104 -0
- package/bin/webui/i18n/TERMS.md +134 -0
- package/bin/webui/i18n/en/banner.json +29 -0
- package/bin/webui/i18n/en/boundary.json +20 -0
- package/bin/webui/i18n/en/challenge.json +106 -0
- package/bin/webui/i18n/en/common.json +69 -0
- package/bin/webui/i18n/en/crosslink.json +69 -0
- package/bin/webui/i18n/en/docs.json +175 -0
- package/bin/webui/i18n/en/experiment.json +17 -0
- package/bin/webui/i18n/en/extra.json +313 -0
- package/bin/webui/i18n/en/flow.json +30 -0
- package/bin/webui/i18n/en/handoff.json +37 -0
- package/bin/webui/i18n/en/knowledge.json +141 -0
- package/bin/webui/i18n/en/learn.json +16 -0
- package/bin/webui/i18n/en/maintenance.json +52 -0
- package/bin/webui/i18n/en/mockrun.json +19 -0
- package/bin/webui/i18n/en/nav.json +19 -0
- package/bin/webui/i18n/en/overview.json +89 -0
- package/bin/webui/i18n/en/pact.json +36 -0
- package/bin/webui/i18n/en/runs.json +51 -0
- package/bin/webui/i18n/en/settings.json +49 -0
- package/bin/webui/i18n/en/stats.json +69 -0
- package/bin/webui/i18n/en/tour.json +33 -0
- package/bin/webui/i18n/id/banner.json +29 -0
- package/bin/webui/i18n/id/boundary.json +20 -0
- package/bin/webui/i18n/id/challenge.json +106 -0
- package/bin/webui/i18n/id/common.json +70 -0
- package/bin/webui/i18n/id/crosslink.json +69 -0
- package/bin/webui/i18n/id/docs.json +175 -0
- package/bin/webui/i18n/id/experiment.json +17 -0
- package/bin/webui/i18n/id/extra.json +313 -0
- package/bin/webui/i18n/id/flow.json +30 -0
- package/bin/webui/i18n/id/handoff.json +37 -0
- package/bin/webui/i18n/id/knowledge.json +141 -0
- package/bin/webui/i18n/id/learn.json +16 -0
- package/bin/webui/i18n/id/maintenance.json +52 -0
- package/bin/webui/i18n/id/mockrun.json +19 -0
- package/bin/webui/i18n/id/nav.json +19 -0
- package/bin/webui/i18n/id/overview.json +89 -0
- package/bin/webui/i18n/id/pact.json +36 -0
- package/bin/webui/i18n/id/runs.json +51 -0
- package/bin/webui/i18n/id/settings.json +49 -0
- package/bin/webui/i18n/id/stats.json +69 -0
- package/bin/webui/i18n/id/tour.json +33 -0
- package/bin/webui/js/00-core.js +115 -0
- package/bin/webui/js/01-i18n.js +149 -0
- package/bin/webui/js/02-ui.js +211 -0
- package/bin/webui/js/03-md.js +286 -0
- package/bin/webui/js/04-router.js +51 -0
- package/bin/webui/js/05-banners.js +172 -0
- package/bin/webui/js/06-edit.js +183 -0
- package/bin/webui/js/90-tour.js +488 -0
- package/bin/webui/js/91-shortcuts.js +126 -0
- package/bin/webui/js/99-boot.js +118 -0
- package/bin/webui/js/panels/boundary.js +104 -0
- package/bin/webui/js/panels/challenge.js +781 -0
- package/bin/webui/js/panels/crosslink.js +654 -0
- package/bin/webui/js/panels/docs.js +1338 -0
- package/bin/webui/js/panels/experiment.js +98 -0
- package/bin/webui/js/panels/extra.js +3011 -0
- package/bin/webui/js/panels/flow.js +297 -0
- package/bin/webui/js/panels/handoff.js +169 -0
- package/bin/webui/js/panels/knowledge.js +890 -0
- package/bin/webui/js/panels/learn.js +220 -0
- package/bin/webui/js/panels/maintenance.js +235 -0
- package/bin/webui/js/panels/mockrun.js +227 -0
- package/bin/webui/js/panels/overview.js +447 -0
- package/bin/webui/js/panels/pact.js +189 -0
- package/bin/webui/js/panels/runs.js +416 -0
- package/bin/webui/js/panels/settings.js +573 -0
- package/bin/webui/js/panels/stats.js +378 -0
- package/bin/webui/serve.js +557 -0
- package/mock-run/INDEX.md +106 -0
- package/mock-run/a-normal-day.md +587 -0
- package/mock-run/context-combiner.md +100 -0
- package/mock-run/extra-recovery.md +330 -0
- package/mock-run/extra-slots.md +177 -0
- package/mock-run/media/README.md +43 -0
- package/mock-run/orc-aftermath.md +392 -0
- package/mock-run/orc-boundary.md +356 -0
- package/mock-run/orc-brainstorm.md +177 -0
- package/mock-run/orc-budget.md +534 -0
- package/mock-run/orc-challenge-council.md +262 -0
- package/mock-run/orc-challenge.md +416 -0
- package/mock-run/orc-cli.md +200 -0
- package/mock-run/orc-diy.md +129 -0
- package/mock-run/orc-doc.md +448 -0
- package/mock-run/orc-explain.md +86 -0
- package/mock-run/orc-export.md +423 -0
- package/mock-run/orc-extra.md +392 -0
- package/mock-run/orc-fast.md +106 -0
- package/mock-run/orc-grill.md +151 -0
- package/mock-run/orc-handoff.md +480 -0
- package/mock-run/orc-pact.md +421 -0
- package/mock-run/orc-pattern.md +112 -0
- package/mock-run/orc-plan.md +108 -0
- package/mock-run/orc-pr-setup.md +126 -0
- package/mock-run/orc-quick.md +113 -0
- package/mock-run/orc-route.md +93 -0
- package/mock-run/orc-ui.md +125 -0
- package/mock-run/orc-ultra.md +103 -0
- package/mock-run/orc.md +157 -0
- package/mock-run/the-example-project.md +55 -0
- package/package.json +39 -0
- package/templates/agents/MODEL-MAPPING.md +168 -0
- package/templates/agents/orc-advisor-fable-5.md +50 -0
- package/templates/agents/orc-advisor-opus-5-xhigh.md +56 -0
- package/templates/agents/orc-analyst-fable-5.md +115 -0
- package/templates/agents/orc-analyze-mini-opus-5-med.md +60 -0
- package/templates/agents/orc-analyze-mini-sonnet-5-high.md +58 -0
- package/templates/agents/orc-challenge-advisor-opus-5-med.md +75 -0
- package/templates/agents/orc-challenge-contrarian-opus-5-high.md +110 -0
- package/templates/agents/orc-challenge-executor-opus-5-med.md +114 -0
- package/templates/agents/orc-challenge-expansionist-opus-5-med.md +112 -0
- package/templates/agents/orc-challenge-judge-opus-5-high.md +132 -0
- package/templates/agents/orc-challenge-outsider-opus-5-low.md +109 -0
- package/templates/agents/orc-challenge-principles-opus-5-high.md +109 -0
- package/templates/agents/orc-challenge-reader-opus-5-low.md +90 -0
- package/templates/agents/orc-claude-writer-opus-4-8-high.md +53 -0
- package/templates/agents/orc-claude-writer-opus-5-med.md +55 -0
- package/templates/agents/orc-context-combiner-opus-5-high.md +88 -0
- package/templates/agents/orc-doc-checker-opus-5-low.md +108 -0
- package/templates/agents/orc-doc-writer-opus-5-med.md +134 -0
- package/templates/agents/orc-executor-haiku-4-5.md +113 -0
- package/templates/agents/orc-executor-opus-4-7-high.md +114 -0
- package/templates/agents/orc-executor-opus-4-7-med.md +114 -0
- package/templates/agents/orc-executor-opus-4-8-high.md +114 -0
- package/templates/agents/orc-executor-opus-5-high.md +114 -0
- package/templates/agents/orc-executor-opus-5-low.md +114 -0
- package/templates/agents/orc-executor-opus-5-med.md +114 -0
- package/templates/agents/orc-executor-sonnet-4-6-high.md +114 -0
- package/templates/agents/orc-executor-sonnet-4-6-med.md +114 -0
- package/templates/agents/orc-executor-sonnet-5-high.md +114 -0
- package/templates/agents/orc-judge-fable-5.md +79 -0
- package/templates/agents/orc-judge-opus-5-xhigh.md +85 -0
- package/templates/agents/orc-learn-writer-opus-5-low.md +73 -0
- package/templates/agents/orc-pattern-codifier-opus-5-med.md +65 -0
- package/templates/agents/orc-pattern-codifier-sonnet-5-high.md +63 -0
- package/templates/agents/orc-planner-fable-5.md +152 -0
- package/templates/agents/orc-planner-mini-opus-5-med.md +69 -0
- package/templates/agents/orc-planner-mini-sonnet-5-high.md +67 -0
- package/templates/agents/orc-planner-opus-5-med.md +160 -0
- package/templates/agents/orc-retro-opus-5-med.md +73 -0
- package/templates/agents/orc-retro-sonnet-5-high.md +72 -0
- package/templates/agents/orc-reviewer-fable-5.md +57 -0
- package/templates/agents/orc-reviewer-opus-5-med.md +60 -0
- package/templates/agents/orc-scout-opus-5-low.md +40 -0
- package/templates/agents/orc-scout-sonnet-4-6-high.md +39 -0
- package/templates/agents/orc-system-analyst-opus-5-high.md +120 -0
- package/templates/agents/orc-test-author-opus-5-med.md +71 -0
- package/templates/agents/orc-trace-writer-haiku-4-5.md +107 -0
- package/templates/agents/orc-verifier-opus-5-med.md +69 -0
- package/templates/agents/orc-wiki-scanner-opus-4-8-high.md +79 -0
- package/templates/agents/orc-wiki-scanner-opus-5-med.md +81 -0
- package/templates/agents/orc-wiki-scanner-sonnet-5-high.md +106 -0
- package/templates/commands/orc-aftermath.md +49 -0
- package/templates/commands/orc-analyze.md +21 -0
- package/templates/commands/orc-boundary.md +46 -0
- package/templates/commands/orc-brainstorm.md +51 -0
- package/templates/commands/orc-budget.md +54 -0
- package/templates/commands/orc-challenge.md +58 -0
- package/templates/commands/orc-claude.md +14 -0
- package/templates/commands/orc-diy.md +13 -0
- package/templates/commands/orc-doc.md +128 -0
- package/templates/commands/orc-explain.md +20 -0
- package/templates/commands/orc-export.md +46 -0
- package/templates/commands/orc-fast.md +15 -0
- package/templates/commands/orc-grill.md +40 -0
- package/templates/commands/orc-handoff.md +52 -0
- package/templates/commands/orc-learn.md +17 -0
- package/templates/commands/orc-mini.md +12 -0
- package/templates/commands/orc-pact.md +44 -0
- package/templates/commands/orc-pattern.md +15 -0
- package/templates/commands/orc-plan.md +22 -0
- package/templates/commands/orc-poly.md +21 -0
- package/templates/commands/orc-pr-driver.md +30 -0
- package/templates/commands/orc-pr-setup.md +31 -0
- package/templates/commands/orc-quick.md +33 -0
- package/templates/commands/orc-retro.md +17 -0
- package/templates/commands/orc-route.md +41 -0
- package/templates/commands/orc-ultra.md +17 -0
- package/templates/commands/orc-verify.md +11 -0
- package/templates/commands/orc-wiki.md +42 -0
- package/templates/commands/orc.md +16 -0
- package/templates/hooks/orc-effort-guard.js +178 -0
- package/templates/hooks/orc-statusline.js +305 -0
- package/templates/hooks/orc-trace.js +471 -0
- package/templates/hooks/orc-update-lib.js +125 -0
- package/templates/skills/_shared/README.md +50 -0
- package/templates/skills/_shared/detecting-artifacts.md +90 -0
- package/templates/skills/_shared/drift-recovery.md +98 -0
- package/templates/skills/_shared/extra-dispatch.md +1051 -0
- package/templates/skills/_shared/fable5-override.md +56 -0
- package/templates/skills/_shared/fallback-handoff.md +42 -0
- package/templates/skills/_shared/gh-stack-commands.md +65 -0
- package/templates/skills/_shared/gotchas.md +177 -0
- package/templates/skills/_shared/interview.md +144 -0
- package/templates/skills/_shared/lane-suspend.md +82 -0
- package/templates/skills/_shared/opus5-only.md +131 -0
- package/templates/skills/_shared/pr-templates.md +106 -0
- package/templates/skills/_shared/read-ladder.md +54 -0
- package/templates/skills/_shared/return-validation.md +145 -0
- package/templates/skills/_shared/smoke-gate.md +28 -0
- package/templates/skills/_shared/stack-plan.md +135 -0
- package/templates/skills/_shared/untrusted-input.md +63 -0
- package/templates/skills/context-combiner/SKILL.md +187 -0
- package/templates/skills/context-combiner/schemas/combined-report.md +78 -0
- package/templates/skills/context-combiner/schemas/combined-requirement-spec.md +87 -0
- package/templates/skills/orc/README.md +150 -0
- package/templates/skills/orc/SKILL.md +526 -0
- package/templates/skills/orc/config.md +389 -0
- package/templates/skills/orc/examples/full-run-mock.md +73 -0
- package/templates/skills/orc/references/analyst-gates.md +124 -0
- package/templates/skills/orc/references/effort-and-mode.md +224 -0
- package/templates/skills/orc/references/house-rules.md +20 -0
- package/templates/skills/orc/references/intake.md +120 -0
- package/templates/skills/orc/references/log-protocol.md +39 -0
- package/templates/skills/orc/references/pattern-gate.md +89 -0
- package/templates/skills/orc/references/plan-handoff.md +84 -0
- package/templates/skills/orc/references/preflight-report.md +220 -0
- package/templates/skills/orc/references/security-checklist.md +39 -0
- package/templates/skills/orc/references/stop-and-resume.md +160 -0
- package/templates/skills/orc/references/trace-protocol.md +314 -0
- package/templates/skills/orc/references/ultra-mode.md +123 -0
- package/templates/skills/orc/references/wave-grouping.md +121 -0
- package/templates/skills/orc/references/wiki-consult.md +177 -0
- package/templates/skills/orc/schemas/checkpoint.md +122 -0
- package/templates/skills/orc/schemas/intent-spec.md +42 -0
- package/templates/skills/orc/schemas/planning-output.md +279 -0
- package/templates/skills/orc/subskills/orc-checkpoint/SKILL.md +55 -0
- package/templates/skills/orc/subskills/orc-execution/SKILL.md +72 -0
- package/templates/skills/orc/subskills/orc-execution/core.md +151 -0
- package/templates/skills/orc/subskills/orc-execution/subagent.md +13 -0
- package/templates/skills/orc/subskills/orc-planner/SKILL.md +264 -0
- package/templates/skills/orc/subskills/orc-planner-mini/SKILL.md +121 -0
- package/templates/skills/orc/subskills/orc-pr/SKILL.md +51 -0
- package/templates/skills/orc/subskills/orc-pr/pr.md +22 -0
- package/templates/skills/orc/subskills/orc-pr/stack-gate.md +108 -0
- package/templates/skills/orc/subskills/orc-review-verify/SKILL.md +76 -0
- package/templates/skills/orc/subskills/orc-review-verify/core.md +135 -0
- package/templates/skills/orc/subskills/orc-review-verify/subagent.md +7 -0
- package/templates/skills/orc/subskills/orc-testgen/SKILL.md +45 -0
- package/templates/skills/orc/subskills/orc-testgen/core.md +59 -0
- package/templates/skills/orc/subskills/orc-testgen/subagent.md +7 -0
- package/templates/skills/orc-advisor/SKILL.md +60 -0
- package/templates/skills/orc-aftermath/SKILL.md +136 -0
- package/templates/skills/orc-aftermath/references/report.md +95 -0
- package/templates/skills/orc-analyze/SKILL.md +220 -0
- package/templates/skills/orc-analyze/examples/analyze-mock.md +42 -0
- package/templates/skills/orc-analyze/references/branching.md +51 -0
- package/templates/skills/orc-analyze/references/deep-mode.md +27 -0
- package/templates/skills/orc-analyze/references/thin-input.md +77 -0
- package/templates/skills/orc-analyze/schemas/report-audit.md +83 -0
- package/templates/skills/orc-analyze/schemas/report-prose.md +63 -0
- package/templates/skills/orc-analyze/schemas/report-requirement.md +78 -0
- package/templates/skills/orc-analyze/schemas/requirement-spec.md +77 -0
- package/templates/skills/orc-analyze-mini/SKILL.md +112 -0
- package/templates/skills/orc-analyze-mini/examples/quick-analysis-mock.md +31 -0
- package/templates/skills/orc-boundary/SKILL.md +208 -0
- package/templates/skills/orc-boundary/references/card.md +78 -0
- package/templates/skills/orc-boundary/references/gate.md +113 -0
- package/templates/skills/orc-brainstorm/SKILL.md +339 -0
- package/templates/skills/orc-brainstorm/references/brainstorm-doc.md +133 -0
- package/templates/skills/orc-brainstorm/references/lenses.md +152 -0
- package/templates/skills/orc-budget/SKILL.md +225 -0
- package/templates/skills/orc-budget/references/corpus.md +87 -0
- package/templates/skills/orc-challenge/README.md +142 -0
- package/templates/skills/orc-challenge/SKILL.md +255 -0
- package/templates/skills/orc-challenge/examples/code-module.md +110 -0
- package/templates/skills/orc-challenge/examples/council-full-roster.md +273 -0
- package/templates/skills/orc-challenge/examples/tsd-two-iterations.md +213 -0
- package/templates/skills/orc-challenge/references/conservation.md +107 -0
- package/templates/skills/orc-challenge/references/council.md +315 -0
- package/templates/skills/orc-challenge/references/cycle-state.md +185 -0
- package/templates/skills/orc-challenge/references/dimensions.md +121 -0
- package/templates/skills/orc-challenge/references/fix-brief.md +130 -0
- package/templates/skills/orc-challenge/references/intake.md +171 -0
- package/templates/skills/orc-challenge/references/kinds.md +44 -0
- package/templates/skills/orc-challenge/references/plain-english.md +98 -0
- package/templates/skills/orc-challenge/references/rubric.md +109 -0
- package/templates/skills/orc-challenge/references/sealed-slice.md +97 -0
- package/templates/skills/orc-challenge/references/verdict-doc.md +154 -0
- package/templates/skills/orc-claude/SKILL.md +199 -0
- package/templates/skills/orc-claude/examples/claude-run-mock.md +65 -0
- package/templates/skills/orc-claude/references/refresh.md +76 -0
- package/templates/skills/orc-claude/references/template.md +109 -0
- package/templates/skills/orc-diy/README.md +142 -0
- package/templates/skills/orc-diy/SKILL.md +70 -0
- package/templates/skills/orc-diy/references/blocks/analyze.md +24 -0
- package/templates/skills/orc-diy/references/blocks/execution.md +42 -0
- package/templates/skills/orc-diy/references/blocks/extra.md +55 -0
- package/templates/skills/orc-diy/references/blocks/header.md +55 -0
- package/templates/skills/orc-diy/references/blocks/mock-example.md +22 -0
- package/templates/skills/orc-diy/references/blocks/pattern.md +18 -0
- package/templates/skills/orc-diy/references/blocks/planning.md +34 -0
- package/templates/skills/orc-diy/references/blocks/review.md +18 -0
- package/templates/skills/orc-diy/references/blocks/scoring.md +16 -0
- package/templates/skills/orc-diy/references/blocks/security.md +25 -0
- package/templates/skills/orc-diy/references/blocks/ship.md +25 -0
- package/templates/skills/orc-diy/references/blocks/summary.md +18 -0
- package/templates/skills/orc-diy/references/blocks/testgen.md +17 -0
- package/templates/skills/orc-diy/references/blocks/trace.md +28 -0
- package/templates/skills/orc-diy/references/blocks/verify.md +25 -0
- package/templates/skills/orc-diy/references/blocks/wiki.md +26 -0
- package/templates/skills/orc-diy/references/compile.md +59 -0
- package/templates/skills/orc-diy/references/flow-schema.md +100 -0
- package/templates/skills/orc-diy/references/locked-blocks.md +37 -0
- package/templates/skills/orc-doc/README.md +229 -0
- package/templates/skills/orc-doc/SKILL.md +444 -0
- package/templates/skills/orc-doc/examples/orc-doc-prd-run.md +325 -0
- package/templates/skills/orc-doc/references/chunking.md +527 -0
- package/templates/skills/orc-doc/references/gates.md +311 -0
- package/templates/skills/orc-doc/references/generation-rules.md +122 -0
- package/templates/skills/orc-doc/references/house-rules.md +170 -0
- package/templates/skills/orc-doc/references/import-targets.md +40 -0
- package/templates/skills/orc-doc/references/plain-language.md +66 -0
- package/templates/skills/orc-doc/references/portable-markdown.md +74 -0
- package/templates/skills/orc-doc/references/resume-protocol.md +228 -0
- package/templates/skills/orc-doc/references/templates/collaboration.md +118 -0
- package/templates/skills/orc-doc/references/templates/prd.md +133 -0
- package/templates/skills/orc-doc/references/templates/report.md +102 -0
- package/templates/skills/orc-doc/references/templates/tsd.md +122 -0
- package/templates/skills/orc-doc/references/templates/workflow.md +112 -0
- package/templates/skills/orc-explain/SKILL.md +57 -0
- package/templates/skills/orc-export/SKILL.md +154 -0
- package/templates/skills/orc-fast/SKILL.md +188 -0
- package/templates/skills/orc-grill/SKILL.md +207 -0
- package/templates/skills/orc-grill/references/grill-doc.md +105 -0
- package/templates/skills/orc-handoff/SKILL.md +204 -0
- package/templates/skills/orc-handoff/references/handoff-log.md +67 -0
- package/templates/skills/orc-handoff/references/surfaces.md +88 -0
- package/templates/skills/orc-judge/SKILL.md +82 -0
- package/templates/skills/orc-learn/SKILL.md +156 -0
- package/templates/skills/orc-learn/examples/learn-run-mock.md +61 -0
- package/templates/skills/orc-learn/references/deepen.md +70 -0
- package/templates/skills/orc-learn/references/refresh.md +65 -0
- package/templates/skills/orc-learn/references/template-knowledge.md +83 -0
- package/templates/skills/orc-learn/references/template-learning.md +67 -0
- package/templates/skills/orc-mini/SKILL.md +226 -0
- package/templates/skills/orc-mini/examples/mini-run-mock.md +50 -0
- package/templates/skills/orc-pact/SKILL.md +218 -0
- package/templates/skills/orc-pact/references/gate.md +70 -0
- package/templates/skills/orc-pact/references/ledger.md +99 -0
- package/templates/skills/orc-pattern/SKILL.md +124 -0
- package/templates/skills/orc-pattern/references/INDEX.md +73 -0
- package/templates/skills/orc-pattern/references/be-django.md +80 -0
- package/templates/skills/orc-pattern/references/be-express.md +82 -0
- package/templates/skills/orc-pattern/references/be-fastapi.md +107 -0
- package/templates/skills/orc-pattern/references/be-go.md +96 -0
- package/templates/skills/orc-pattern/references/be-nestjs.md +86 -0
- package/templates/skills/orc-pattern/references/be-postgres.md +98 -0
- package/templates/skills/orc-pattern/references/fe-a11y.md +36 -0
- package/templates/skills/orc-pattern/references/fe-angular.md +87 -0
- package/templates/skills/orc-pattern/references/fe-nextjs.md +65 -0
- package/templates/skills/orc-pattern/references/fe-perf.md +39 -0
- package/templates/skills/orc-pattern/references/fe-react.md +83 -0
- package/templates/skills/orc-pattern/references/fe-vue.md +75 -0
- package/templates/skills/orc-pattern/schemas/pattern-doc.md +62 -0
- package/templates/skills/orc-poly/SKILL.md +216 -0
- package/templates/skills/orc-poly/examples/poly-run-mock.md +51 -0
- package/templates/skills/orc-poly/references/gather.md +70 -0
- package/templates/skills/orc-poly/references/poly-spec.md +84 -0
- package/templates/skills/orc-pr-driver/README.md +349 -0
- package/templates/skills/orc-pr-driver/SKILL.md +167 -0
- package/templates/skills/orc-pr-driver/references/conflict-playbook.md +78 -0
- package/templates/skills/orc-pr-driver/references/green-gate.md +105 -0
- package/templates/skills/orc-pr-driver/references/orc-run-split.md +99 -0
- package/templates/skills/orc-pr-setup/README.md +436 -0
- package/templates/skills/orc-pr-setup/SKILL.md +184 -0
- package/templates/skills/orc-pr-setup/references/certainty-gate.md +66 -0
- package/templates/skills/orc-pr-setup/references/layer-taxonomy.md +67 -0
- package/templates/skills/orc-quick/README.md +423 -0
- package/templates/skills/orc-quick/SKILL.md +306 -0
- package/templates/skills/orc-quick/references/context-doc.md +114 -0
- package/templates/skills/orc-quick/references/dispatch-gate.md +163 -0
- package/templates/skills/orc-quick/references/gh-mode.md +127 -0
- package/templates/skills/orc-retro/SKILL.md +222 -0
- package/templates/skills/orc-retro/examples/retro-mock.md +171 -0
- package/templates/skills/orc-route/SKILL.md +165 -0
- package/templates/skills/orc-verify/SKILL.md +86 -0
- package/templates/skills/orc-verify/examples/verify-mock.md +33 -0
- package/templates/skills/orc-wiki/README.md +325 -0
- package/templates/skills/orc-wiki/SKILL.md +322 -0
- package/templates/skills/orc-wiki/examples/wiki-run-mock.md +37 -0
- package/templates/skills/orc-wiki/references/claude-md-injection.md +61 -0
- package/templates/skills/orc-wiki/references/crosslink-compile.md +39 -0
- package/templates/skills/orc-wiki/references/crosslink-kinds.md +57 -0
- package/templates/skills/orc-wiki/references/crosslink.md +284 -0
- package/templates/skills/orc-wiki/references/extra.md +79 -0
- package/templates/skills/orc-wiki/references/integrity-check.md +86 -0
- package/templates/skills/orc-wiki/references/orientation.md +51 -0
- package/templates/skills/orc-wiki/references/partial-refresh.md +203 -0
- package/templates/skills/orc-wiki/references/pattern-prewarm.md +19 -0
- package/templates/skills/orc-wiki/references/staleness.md +288 -0
- package/templates/skills/orc-wiki/schemas/crosslink-tag.md +166 -0
- package/templates/skills/orc-wiki/schemas/wiki-doc.md +120 -0
|
@@ -0,0 +1,654 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/* panels/crosslink.js — orc ui client
|
|
3
|
+
Design and Settings. VAULT.W/H must stay the same box as .vault-node's CSS
|
|
4
|
+
width/height — a test asserts it and re-runs ringRadii over n=1..16.
|
|
5
|
+
|
|
6
|
+
Loaded by app.html in the order its numeric prefix names. Classic
|
|
7
|
+
script, no import/export: an ES module import carries no query string,
|
|
8
|
+
and every static request here needs the per-launch session token. */
|
|
9
|
+
|
|
10
|
+
/* =============================================================== CROSSLINK == */
|
|
11
|
+
|
|
12
|
+
PANELS.crosslink = function (host) {
|
|
13
|
+
head(host, t("crosslink.title"), t("crosslink.sub"));
|
|
14
|
+
const body = el("div", "stack");
|
|
15
|
+
host.append(body);
|
|
16
|
+
renderCrosslink(body);
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
// TWO TABS (v0.43.7): DESIGN is the picture of the boundary, SETTINGS is every
|
|
20
|
+
// control. They were one scrolling column, which made the diagram something you
|
|
21
|
+
// scrolled past on the way to the add form rather than the thing you came for.
|
|
22
|
+
//
|
|
23
|
+
// With nothing linked yet there is no picture to draw, so Settings opens
|
|
24
|
+
// selected and its tab is spotlighted — Design stays reachable and says, in the
|
|
25
|
+
// empty state, exactly which tab makes it appear.
|
|
26
|
+
async function renderCrosslink(body) {
|
|
27
|
+
body.replaceChildren(skeleton(5));
|
|
28
|
+
const d = (await read("/api/crosslink")).data;
|
|
29
|
+
const live = d.configured && d.nodes.length > 0;
|
|
30
|
+
|
|
31
|
+
const tabs = el("div", "tabs");
|
|
32
|
+
// `stack` as well as `tab-pane`: the Settings tab holds several cards, and
|
|
33
|
+
// the container is what spaces panel blocks — see `.stack` in app.css.
|
|
34
|
+
const pane = el("div", "tab-pane stack");
|
|
35
|
+
const views = {
|
|
36
|
+
design: () => designView(d, live, () => select("settings")),
|
|
37
|
+
settings: () => settingsView(d, body),
|
|
38
|
+
};
|
|
39
|
+
const select = async (which) => {
|
|
40
|
+
for (const b of tabs.children) b.setAttribute("aria-selected", String(b.dataset.tab === which));
|
|
41
|
+
pane.replaceChildren(skeleton(3));
|
|
42
|
+
const built = await views[which]();
|
|
43
|
+
pane.replaceChildren(built);
|
|
44
|
+
};
|
|
45
|
+
// Keys are written out in full, never assembled from the tab id — a key built
|
|
46
|
+
// from a fragment is invisible to every check that looks for one.
|
|
47
|
+
for (const [which, label] of [["design", t("crosslink.tab.design")], ["settings", t("crosslink.tab.settings")]]) {
|
|
48
|
+
const b = el("button", null, label);
|
|
49
|
+
b.type = "button";
|
|
50
|
+
b.dataset.tab = which;
|
|
51
|
+
// The spotlight is the answer to "there is nothing here" — it points at the
|
|
52
|
+
// one tab that can change that, and it is dropped the moment a link exists.
|
|
53
|
+
if (!live && which === "settings") b.classList.add("tab-spot");
|
|
54
|
+
b.addEventListener("click", () => select(which));
|
|
55
|
+
tabs.append(b);
|
|
56
|
+
}
|
|
57
|
+
body.replaceChildren(tabs, pane);
|
|
58
|
+
select(live ? "design" : "settings");
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function designView(d, live, gotoSettings) {
|
|
62
|
+
const out = frag();
|
|
63
|
+
if (!live) {
|
|
64
|
+
const e = empty(t("crosslink.design.empty"), t("crosslink.design.emptyHint"));
|
|
65
|
+
const go = el("button", "btn btn-sm btn-primary", t("crosslink.design.emptyCta"));
|
|
66
|
+
go.type = "button";
|
|
67
|
+
go.addEventListener("click", gotoSettings);
|
|
68
|
+
e.append(go);
|
|
69
|
+
out.append(e);
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
out.append(vaultCard(d));
|
|
73
|
+
return out;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
async function settingsView(d, body) {
|
|
77
|
+
const out = frag();
|
|
78
|
+
|
|
79
|
+
if (!d.configured) {
|
|
80
|
+
out.append(empty(t("crosslink.empty"), t("crosslink.emptyHint")));
|
|
81
|
+
out.append(await addLinkCard(d, body));
|
|
82
|
+
return out;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
const head2 = card(t("crosslink.graph"));
|
|
86
|
+
head2.append(
|
|
87
|
+
kvList(
|
|
88
|
+
[
|
|
89
|
+
[t("crosslink.self"), d.self],
|
|
90
|
+
[t("crosslink.config"), d.config_path],
|
|
91
|
+
[t("crosslink.needs"), d.needs_baseline || t("crosslink.needsNone")],
|
|
92
|
+
],
|
|
93
|
+
true
|
|
94
|
+
)
|
|
95
|
+
);
|
|
96
|
+
out.append(head2);
|
|
97
|
+
|
|
98
|
+
out.append(await addLinkCard(d, body));
|
|
99
|
+
|
|
100
|
+
for (const n of d.nodes) {
|
|
101
|
+
const c = el("div", "action");
|
|
102
|
+
c.dataset.node = n.name; // paired with the graph node above on hover
|
|
103
|
+
const left = el("div");
|
|
104
|
+
const name = el("div", "setting-name");
|
|
105
|
+
name.append(document.createTextNode(n.name));
|
|
106
|
+
name.append(
|
|
107
|
+
chip(
|
|
108
|
+
n.direction === "consume" ? t("crosslink.weCall") : n.direction === "provide" ? t("crosslink.theyCall") : t("crosslink.noEdge"),
|
|
109
|
+
n.direction === "consume" ? "info" : n.direction === "provide" ? "" : "warn"
|
|
110
|
+
)
|
|
111
|
+
);
|
|
112
|
+
left.append(name);
|
|
113
|
+
// repo_path and kind ids come straight from the config file.
|
|
114
|
+
left.append(el("div", "setting-desc", n.repo_path + (n.kinds.length ? " · kinds: " + n.kinds.join(", ") : "")));
|
|
115
|
+
const pv = n.provider || {};
|
|
116
|
+
if (pv.state === "missing") left.append(el("div", "note", t("crosslink.state.missing")));
|
|
117
|
+
else if (pv.state === "no-wiki") left.append(el("div", "note", t("crosslink.state.noWiki")));
|
|
118
|
+
else if (pv.state === "unregistered") left.append(el("div", "note", t("crosslink.state.unregistered")));
|
|
119
|
+
else if (pv.state === "corrupt") left.append(el("div", "note", t("crosslink.state.corrupt")));
|
|
120
|
+
else if (n.direction === "provide") left.append(el("div", "note", t("crosslink.state.inbound")));
|
|
121
|
+
else {
|
|
122
|
+
const row = el("div", "row-actions");
|
|
123
|
+
row.append(chip(pv.tier || t("overview.tile.wikiUnknown"), pv.tier === "FRESH" ? "ok" : pv.tier === "AGING" ? "warn" : "bad", pv.tier === "STALE"));
|
|
124
|
+
row.append(el("span", "note", t("crosslink.peerNote", { scan: pv.last_scan || "?", tags: pv.tags || 0 })));
|
|
125
|
+
left.append(row);
|
|
126
|
+
if (!pv.tags) left.append(el("div", "note", t("crosslink.noTags", { path: n.repo_path })));
|
|
127
|
+
}
|
|
128
|
+
const rm = el("button", "btn btn-sm btn-danger", t("common.remove"));
|
|
129
|
+
rm.type = "button";
|
|
130
|
+
rm.addEventListener("click", () => confirmRemove(n.name, body));
|
|
131
|
+
c.append(left, rm);
|
|
132
|
+
out.append(c);
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
if (d.links.length) {
|
|
136
|
+
const lc = card(t("crosslink.edges"));
|
|
137
|
+
for (const l of d.links) lc.append(el("div", "note", `${l.from} ──${l.via}──▶ ${l.to} (${l.relation.replace(/-/g, " ")})`));
|
|
138
|
+
out.append(lc);
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
return out;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/* ------------------------------------------------------- the vault graph -- */
|
|
145
|
+
|
|
146
|
+
// THE DESIGN TAB (v0.43.7). Self in the middle, every linked repo on a ring
|
|
147
|
+
// around it, one line per edge drawn in the direction it actually points, and a
|
|
148
|
+
// pulse travelling that line so "which way does this one go" is answered by
|
|
149
|
+
// motion instead of by reading an arrow glyph.
|
|
150
|
+
//
|
|
151
|
+
// The layout is COMPUTED, not simulated: peer i sits at a fixed angle for a
|
|
152
|
+
// given node count, so the same config draws the same picture every time you
|
|
153
|
+
// open the tab. A physics sim would be prettier to poke and useless to compare.
|
|
154
|
+
//
|
|
155
|
+
// THE LAYOUT IS SOLVED IN PIXELS FROM THE REAL BOX SIZE, and that is the whole
|
|
156
|
+
// fix in v0.43.7's first patch. Placing fixed-pixel boxes on a ring whose radius
|
|
157
|
+
// was a FRACTION of the container ("0.34 of the height") is a guess: nothing in
|
|
158
|
+
// it knows how wide a repo box is, so at three peers the boxes overlapped and
|
|
159
|
+
// the picture was unreadable. Repo boxes are a fixed size now and the radii are
|
|
160
|
+
// derived from that size, so separation is a property of the layout rather than
|
|
161
|
+
// a value that happened to look right in one screenshot. See ringRadii().
|
|
162
|
+
//
|
|
163
|
+
// Edges carry pathLength="1", so the draw-in and the travelling pulse are
|
|
164
|
+
// written as fractions of the line and never need its length measured.
|
|
165
|
+
|
|
166
|
+
// Fixed box metrics. Fixed is what makes the geometry solvable — a box that
|
|
167
|
+
// sizes to its longest repo name cannot be spaced by arithmetic, only measured
|
|
168
|
+
// and re-measured. Long names ellipsis instead.
|
|
169
|
+
const VAULT = { W: 156, H: 78, GAP: 20, PAD: 24 };
|
|
170
|
+
|
|
171
|
+
// The smallest ellipse on which no two boxes — and no box and the hub — can
|
|
172
|
+
// overlap.
|
|
173
|
+
//
|
|
174
|
+
// Two axis-aligned boxes miss each other when |dx| >= W+GAP OR |dy| >= H+GAP.
|
|
175
|
+
// For neighbours Δ apart on the ring with midangle m:
|
|
176
|
+
// dx = rx·2sin(Δ/2)·|sin m| dy = ry·2sin(Δ/2)·|cos m|
|
|
177
|
+
// One of |sin m|, |cos m| is always at least 1/√2, so scaling both radii by √2
|
|
178
|
+
// makes whichever term is doing the work clear its threshold on its own. The
|
|
179
|
+
// same √2 as a FLOOR handles the hub, which sits at the centre and is a box too.
|
|
180
|
+
// Wider angular gaps only push boxes further apart, so neighbours are the worst
|
|
181
|
+
// case and checking them is enough.
|
|
182
|
+
function ringRadii(n) {
|
|
183
|
+
const needX = Math.SQRT2 * (VAULT.W + VAULT.GAP);
|
|
184
|
+
const needY = Math.SQRT2 * (VAULT.H + VAULT.GAP);
|
|
185
|
+
// n === 1 has no neighbour pair — and sin(π/1) is 0, so the general form
|
|
186
|
+
// divides by zero. One peer only ever has to clear the hub.
|
|
187
|
+
const sep = n > 1 ? 2 * Math.sin(Math.PI / n) : Infinity;
|
|
188
|
+
return { rx: Math.max(needX, needX / sep), ry: Math.max(needY, needY / sep) };
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function vaultCard(d) {
|
|
192
|
+
const summary = t(d.nodes.length === 1 && d.links.length === 1 ? "crosslink.repos" : "crosslink.reposPlural", {
|
|
193
|
+
repos: d.nodes.length,
|
|
194
|
+
edges: d.links.length,
|
|
195
|
+
});
|
|
196
|
+
const c = card(t("crosslink.design.title"), chip(summary, "info"));
|
|
197
|
+
c.append(el("div", "note", t("crosslink.design.note")));
|
|
198
|
+
|
|
199
|
+
const n = d.nodes.length;
|
|
200
|
+
const { rx, ry } = ringRadii(n);
|
|
201
|
+
// Lay out around an origin first, then size the canvas to what was actually
|
|
202
|
+
// placed. Deriving it from the radii instead would pad for a full ellipse even
|
|
203
|
+
// when the ring only uses part of one — a single peer would sit in a box wide
|
|
204
|
+
// enough for six. The ring opens at -90°, so peer 0 is directly above.
|
|
205
|
+
const at = [[0, 0]]; // the hub
|
|
206
|
+
for (let i = 0; i < n; i++) {
|
|
207
|
+
const a = -Math.PI / 2 + (i * 2 * Math.PI) / n;
|
|
208
|
+
at.push([rx * Math.cos(a), ry * Math.sin(a)]);
|
|
209
|
+
}
|
|
210
|
+
const half = [VAULT.W / 2, VAULT.H / 2];
|
|
211
|
+
const bound = (axis) => {
|
|
212
|
+
const lo = Math.min(...at.map((p) => p[axis] - half[axis]));
|
|
213
|
+
const hi = Math.max(...at.map((p) => p[axis] + half[axis]));
|
|
214
|
+
return [lo, hi - lo];
|
|
215
|
+
};
|
|
216
|
+
const [minX, spanX] = bound(0);
|
|
217
|
+
const [minY, spanY] = bound(1);
|
|
218
|
+
const boxW = Math.round(spanX + 2 * VAULT.PAD);
|
|
219
|
+
const boxH = Math.round(spanY + 2 * VAULT.PAD);
|
|
220
|
+
// Origin-relative → canvas pixels.
|
|
221
|
+
const put = (p) => [p[0] - minX + VAULT.PAD, p[1] - minY + VAULT.PAD];
|
|
222
|
+
|
|
223
|
+
// Positions are pixel centres. `self` and the repo's real name both map to the
|
|
224
|
+
// hub because a link may name this repo either way.
|
|
225
|
+
const hub = put(at[0]);
|
|
226
|
+
const pos = { [d.self]: hub, self: hub };
|
|
227
|
+
d.nodes.forEach((node, i) => {
|
|
228
|
+
pos[node.name] = put(at[i + 1]);
|
|
229
|
+
});
|
|
230
|
+
|
|
231
|
+
// A ring big enough not to collide can be wider than the panel. That is a
|
|
232
|
+
// scroll, not a reason to squeeze the boxes back together.
|
|
233
|
+
const scroll = el("div", "scroll-x");
|
|
234
|
+
const wrap = el("div", "vault");
|
|
235
|
+
wrap.style.width = boxW + "px";
|
|
236
|
+
wrap.style.height = boxH + "px";
|
|
237
|
+
|
|
238
|
+
const svg = svgEl("svg", { class: "vault-edges", viewBox: `0 0 ${boxW} ${boxH}` });
|
|
239
|
+
d.links.forEach((l, i) => {
|
|
240
|
+
// `from`/`to` may name this repo either as the literal "self" or by its
|
|
241
|
+
// real name — the config accepts both, so the position map holds both.
|
|
242
|
+
const a = pos[l.from];
|
|
243
|
+
const b = pos[l.to];
|
|
244
|
+
if (!a || !b) return; // an edge naming a repo that is not in the graph
|
|
245
|
+
const g = svgEl("g", { class: "vault-edge", "data-a": l.from, "data-b": l.to });
|
|
246
|
+
g.style.setProperty("--d", i * 140 + "ms");
|
|
247
|
+
// The canvas is a known pixel size, so the viewBox is 1:1 with it and the
|
|
248
|
+
// endpoints go straight in — no measuring pass, and no stretched viewBox
|
|
249
|
+
// squashing the labels and strokes.
|
|
250
|
+
const ends = { x1: a[0].toFixed(1), y1: a[1].toFixed(1), x2: b[0].toFixed(1), y2: b[1].toFixed(1), pathLength: "1" };
|
|
251
|
+
g.append(svgEl("line", { ...ends, class: "vault-line" }));
|
|
252
|
+
// The pulse runs from → to, which IS the direction of the dependency.
|
|
253
|
+
g.append(svgEl("line", { ...ends, class: "vault-pulse" }));
|
|
254
|
+
if (l.via) {
|
|
255
|
+
// Nudged off the line so the label sits beside it, not on top of it.
|
|
256
|
+
const label = svgEl("text", {
|
|
257
|
+
class: "vault-via",
|
|
258
|
+
x: ((a[0] + b[0]) / 2).toFixed(1),
|
|
259
|
+
y: ((a[1] + b[1]) / 2 - 8).toFixed(1),
|
|
260
|
+
"text-anchor": "middle",
|
|
261
|
+
});
|
|
262
|
+
label.textContent = l.via;
|
|
263
|
+
g.append(label);
|
|
264
|
+
}
|
|
265
|
+
svg.append(g);
|
|
266
|
+
});
|
|
267
|
+
wrap.append(svg);
|
|
268
|
+
|
|
269
|
+
const place = (node, p, i) => {
|
|
270
|
+
node.style.left = p[0].toFixed(1) + "px";
|
|
271
|
+
node.style.top = p[1].toFixed(1) + "px";
|
|
272
|
+
node.style.setProperty("--d", i * 90 + "ms");
|
|
273
|
+
wrap.append(node);
|
|
274
|
+
};
|
|
275
|
+
|
|
276
|
+
const self = el("div", "vault-node vault-hub");
|
|
277
|
+
self.append(el("span", "vault-name", d.self), el("span", "vault-sub", t("crosslink.design.thisRepo")));
|
|
278
|
+
place(self, pos[d.self], 0);
|
|
279
|
+
|
|
280
|
+
d.nodes.forEach((node, i) => {
|
|
281
|
+
const box = el("button", "vault-node vault-" + (node.direction || "none"));
|
|
282
|
+
box.type = "button";
|
|
283
|
+
box.dataset.name = node.name;
|
|
284
|
+
box.append(el("span", "vault-name", node.name));
|
|
285
|
+
box.append(el("span", "vault-sub", node.repo_path));
|
|
286
|
+
const pv = node.provider || {};
|
|
287
|
+
// The chip carries the CLI's own state word, so the picture and `orc
|
|
288
|
+
// crosslink list` always say the same thing.
|
|
289
|
+
const state =
|
|
290
|
+
pv.state === "missing" ? ["missing", "bad"] :
|
|
291
|
+
pv.state === "no-wiki" ? ["no wiki", "warn"] :
|
|
292
|
+
pv.state === "unregistered" ? ["unregistered", "warn"] :
|
|
293
|
+
pv.state === "corrupt" ? ["corrupt", "bad"] :
|
|
294
|
+
node.direction === "provide" ? ["inbound", ""] :
|
|
295
|
+
[pv.tier || "linked", pv.tier === "FRESH" ? "ok" : pv.tier === "AGING" ? "warn" : "bad"];
|
|
296
|
+
box.append(chip(state[0], state[1]));
|
|
297
|
+
box.title = `${node.name} — ${node.repo_path}`;
|
|
298
|
+
|
|
299
|
+
// Hovering a repo lights the edges it is an end of, so a busy ring can still
|
|
300
|
+
// be read one repo at a time.
|
|
301
|
+
const hi = (onOff) => {
|
|
302
|
+
box.classList.toggle("vault-hi", onOff);
|
|
303
|
+
for (const g of svg.querySelectorAll(".vault-edge")) {
|
|
304
|
+
if (g.dataset.a === node.name || g.dataset.b === node.name) g.classList.toggle("edge-hi", onOff);
|
|
305
|
+
else g.classList.toggle("edge-dim", onOff);
|
|
306
|
+
}
|
|
307
|
+
};
|
|
308
|
+
box.addEventListener("mouseenter", () => hi(true));
|
|
309
|
+
box.addEventListener("mouseleave", () => hi(false));
|
|
310
|
+
box.addEventListener("focus", () => hi(true));
|
|
311
|
+
box.addEventListener("blur", () => hi(false));
|
|
312
|
+
// Clicking a repo is a question about that repo — the answer (its state, its
|
|
313
|
+
// kinds, the Remove button) lives on the Settings tab, so go there.
|
|
314
|
+
box.addEventListener("click", () => {
|
|
315
|
+
const tab = document.querySelector('.tabs button[data-tab="settings"]');
|
|
316
|
+
if (!tab) return;
|
|
317
|
+
tab.click();
|
|
318
|
+
setTimeout(() => {
|
|
319
|
+
const row = document.querySelector('.action[data-node="' + node.name + '"]');
|
|
320
|
+
if (!row) return;
|
|
321
|
+
row.scrollIntoView({ behavior: "smooth", block: "center" });
|
|
322
|
+
row.classList.add("linked-hi");
|
|
323
|
+
setTimeout(() => row.classList.remove("linked-hi"), 1600);
|
|
324
|
+
}, 120);
|
|
325
|
+
});
|
|
326
|
+
place(box, pos[node.name], i + 1);
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
scroll.append(wrap);
|
|
330
|
+
c.append(scroll);
|
|
331
|
+
return c;
|
|
332
|
+
}
|
|
333
|
+
|
|
334
|
+
function svgEl(tag, attrs) {
|
|
335
|
+
const node = document.createElementNS("http://www.w3.org/2000/svg", tag);
|
|
336
|
+
for (const [k, v] of Object.entries(attrs || {})) node.setAttribute(k, v);
|
|
337
|
+
return node;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// The add form. It mirrors the interactive CLI prompt field for field, and it
|
|
341
|
+
// submits to the CLI rather than writing YAML — so the errors shown here are
|
|
342
|
+
// the CLI's own, not a second opinion about what is valid.
|
|
343
|
+
async function addLinkCard(d, body) {
|
|
344
|
+
const c = card(t("crosslink.add.title"));
|
|
345
|
+
c.append(el("div", "note", t("crosslink.add.note")));
|
|
346
|
+
|
|
347
|
+
const form = el("div", "linkform");
|
|
348
|
+
const mk = (labelText, node, hint) => {
|
|
349
|
+
// `label` wraps its control, and a label that CONTAINS a button steals that
|
|
350
|
+
// button's click — so the path row (input + Browse) is a plain div instead.
|
|
351
|
+
const f = el(node.dataset && node.dataset.nolabel ? "div" : "label", "field");
|
|
352
|
+
f.append(el("span", "field-label", labelText));
|
|
353
|
+
f.append(node);
|
|
354
|
+
if (hint) f.append(el("span", "field-hint", hint));
|
|
355
|
+
return f;
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
const name = el("input", "text-input");
|
|
359
|
+
name.placeholder = "service-z";
|
|
360
|
+
|
|
361
|
+
// THE PATH FIELD (v0.43.6). A hand-typed path is the one field here whose
|
|
362
|
+
// mistakes are invisible: the CLI accepts an unresolvable path on purpose
|
|
363
|
+
// (it saves a PENDING edge that resolves later), so a typo does not fail —
|
|
364
|
+
// it just silently never links. The Browse button removes the typo entirely.
|
|
365
|
+
// It is still a plain text input: browsing is an ADDITION, never the only
|
|
366
|
+
// way in, and pasting a path you already know stays the fastest route.
|
|
367
|
+
const repoPath = el("input", "text-input");
|
|
368
|
+
repoPath.placeholder = "../service-z";
|
|
369
|
+
const pathRow = el("div", "path-row");
|
|
370
|
+
pathRow.dataset.nolabel = "1";
|
|
371
|
+
const browse = el("button", "btn btn-sm", t("crosslink.add.browse"));
|
|
372
|
+
browse.type = "button";
|
|
373
|
+
browse.addEventListener("click", () =>
|
|
374
|
+
pickFolder((rel) => {
|
|
375
|
+
repoPath.value = rel;
|
|
376
|
+
syncCmd();
|
|
377
|
+
})
|
|
378
|
+
);
|
|
379
|
+
pathRow.append(repoPath, browse);
|
|
380
|
+
|
|
381
|
+
// The catalog comes from the CLI so the picker can never drift from it.
|
|
382
|
+
let kinds = [];
|
|
383
|
+
try {
|
|
384
|
+
kinds = (await read("/api/crosslink/kinds")).data.kinds || [];
|
|
385
|
+
} catch (_) {}
|
|
386
|
+
const kindBox = el("div", "kind-picker");
|
|
387
|
+
const picked = new Set();
|
|
388
|
+
for (const k of kinds) {
|
|
389
|
+
const b = el("button", "kind", k);
|
|
390
|
+
b.type = "button";
|
|
391
|
+
b.addEventListener("click", () => {
|
|
392
|
+
if (picked.has(k)) picked.delete(k);
|
|
393
|
+
else picked.add(k);
|
|
394
|
+
b.classList.toggle("kind-on", picked.has(k));
|
|
395
|
+
syncVia();
|
|
396
|
+
});
|
|
397
|
+
kindBox.append(b);
|
|
398
|
+
}
|
|
399
|
+
const custom = el("input", "text-input");
|
|
400
|
+
custom.placeholder = t("crosslink.add.customHint");
|
|
401
|
+
|
|
402
|
+
const dirSeg = el("div", "seg");
|
|
403
|
+
let direction = "calls";
|
|
404
|
+
const self = d.self || "this repo";
|
|
405
|
+
const dirs = [
|
|
406
|
+
["calls", t("crosslink.add.dirCalls", { self })],
|
|
407
|
+
["called-by", t("crosslink.add.dirCalledBy", { self })],
|
|
408
|
+
];
|
|
409
|
+
for (const [val, label] of dirs) {
|
|
410
|
+
const b = el("button", null, label);
|
|
411
|
+
b.type = "button";
|
|
412
|
+
b.setAttribute("aria-pressed", String(val === direction));
|
|
413
|
+
b.addEventListener("click", () => {
|
|
414
|
+
direction = val;
|
|
415
|
+
for (const other of dirSeg.children) other.setAttribute("aria-pressed", "false");
|
|
416
|
+
b.setAttribute("aria-pressed", "true");
|
|
417
|
+
});
|
|
418
|
+
dirSeg.append(b);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
const via = el("select", "text-input");
|
|
422
|
+
const syncVia = () => {
|
|
423
|
+
const all = [...picked, ...custom.value.split(",").map((x) => x.trim().toLowerCase()).filter(Boolean)];
|
|
424
|
+
via.replaceChildren();
|
|
425
|
+
for (const k of [...new Set(all)]) {
|
|
426
|
+
const o = el("option", null, k);
|
|
427
|
+
o.value = k;
|
|
428
|
+
via.append(o);
|
|
429
|
+
}
|
|
430
|
+
via.disabled = !all.length;
|
|
431
|
+
};
|
|
432
|
+
custom.addEventListener("input", syncVia);
|
|
433
|
+
syncVia();
|
|
434
|
+
|
|
435
|
+
form.append(
|
|
436
|
+
mk(t("crosslink.add.name"), name, t("crosslink.add.nameHint")),
|
|
437
|
+
mk(t("crosslink.add.path"), pathRow, t("crosslink.add.pathHint")),
|
|
438
|
+
mk(t("crosslink.add.kinds"), kindBox, t("crosslink.add.kindsHint")),
|
|
439
|
+
mk(t("crosslink.add.custom"), custom),
|
|
440
|
+
mk(t("crosslink.add.direction"), dirSeg, t("crosslink.add.directionHint")),
|
|
441
|
+
mk(t("crosslink.add.via"), via, t("crosslink.add.viaHint"))
|
|
442
|
+
);
|
|
443
|
+
c.append(form);
|
|
444
|
+
|
|
445
|
+
const err = el("div", "input-err");
|
|
446
|
+
const actions = el("div", "row-actions");
|
|
447
|
+
const save = el("button", "btn btn-primary", t("crosslink.add.save"));
|
|
448
|
+
save.type = "button";
|
|
449
|
+
const cmdPreview = el("code", "action-cmd", "");
|
|
450
|
+
const syncCmd = () => {
|
|
451
|
+
const all = [...picked, ...custom.value.split(",").map((x) => x.trim().toLowerCase()).filter(Boolean)];
|
|
452
|
+
cmdPreview.textContent =
|
|
453
|
+
`orc crosslink add ${name.value || "<name>"} ${repoPath.value || "<path>"} ` +
|
|
454
|
+
`--kinds ${all.join(",") || "<kinds>"} --direction ${direction}` +
|
|
455
|
+
(via.value ? ` --via ${via.value}` : "");
|
|
456
|
+
};
|
|
457
|
+
for (const n of [name, repoPath, custom]) n.addEventListener("input", syncCmd);
|
|
458
|
+
via.addEventListener("change", syncCmd);
|
|
459
|
+
kindBox.addEventListener("click", syncCmd);
|
|
460
|
+
dirSeg.addEventListener("click", syncCmd);
|
|
461
|
+
syncCmd();
|
|
462
|
+
|
|
463
|
+
save.addEventListener("click", async () => {
|
|
464
|
+
err.textContent = "";
|
|
465
|
+
const all = [...picked, ...custom.value.split(",").map((x) => x.trim().toLowerCase()).filter(Boolean)];
|
|
466
|
+
// Only the empties are caught here. Everything about VALIDITY — the slug
|
|
467
|
+
// shape, a taken name, an unknown target — is the CLI's call, reported below.
|
|
468
|
+
if (!name.value.trim() || !repoPath.value.trim() || !all.length) {
|
|
469
|
+
err.textContent = t("crosslink.add.required");
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
save.disabled = true;
|
|
473
|
+
save.textContent = t("crosslink.add.saving");
|
|
474
|
+
try {
|
|
475
|
+
const r = await post("/api/crosslink/add", {
|
|
476
|
+
name: name.value.trim(),
|
|
477
|
+
repo_path: repoPath.value.trim(),
|
|
478
|
+
kinds: all.join(","),
|
|
479
|
+
direction,
|
|
480
|
+
via: via.value || all[0],
|
|
481
|
+
});
|
|
482
|
+
if (!r.ok) {
|
|
483
|
+
// The CLI's own rejection text wins over ours whenever there is one.
|
|
484
|
+
err.textContent = r.output || t("crosslink.add.refused");
|
|
485
|
+
save.disabled = false;
|
|
486
|
+
save.textContent = t("crosslink.add.save");
|
|
487
|
+
return;
|
|
488
|
+
}
|
|
489
|
+
toast(t("crosslink.add.linked", { name: name.value.trim() }), "ok", r.output);
|
|
490
|
+
await renderCrosslink(body);
|
|
491
|
+
// The new node draws itself in — the one moment where motion is the
|
|
492
|
+
// feedback that the link now exists.
|
|
493
|
+
const fresh = document.querySelector('.graph-node[data-name="' + name.value.trim() + '"]');
|
|
494
|
+
if (fresh) fresh.classList.add("graph-new");
|
|
495
|
+
} catch (e) {
|
|
496
|
+
err.textContent = String(e.message);
|
|
497
|
+
save.disabled = false;
|
|
498
|
+
save.textContent = t("crosslink.add.save");
|
|
499
|
+
}
|
|
500
|
+
});
|
|
501
|
+
actions.append(save);
|
|
502
|
+
c.append(cmdPreview, err, actions);
|
|
503
|
+
return c;
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
/* --- the folder picker ------------------------------------------------------
|
|
507
|
+
A browser cannot hand back a real filesystem path — `<input type="file"
|
|
508
|
+
webkitdirectory>` gives a folder NAME and nothing above it, which is exactly
|
|
509
|
+
the part a relative repo path needs. So the picker walks the filesystem on
|
|
510
|
+
the SERVER (`/api/fs/list`, directory names only) and the browser just
|
|
511
|
+
renders it. That also makes it identical on Windows and macOS: the server
|
|
512
|
+
knows the real separator and computes the stored relative path itself, so
|
|
513
|
+
nothing here has to guess whether to write `..\peer` or `../peer`.
|
|
514
|
+
|
|
515
|
+
It never picks a FILE and never opens one. `onPick` receives the relative
|
|
516
|
+
path the crosslink config will store — the same string you would have typed. */
|
|
517
|
+
function pickFolder(onPick) {
|
|
518
|
+
const body = el("div", "stack stack-sm");
|
|
519
|
+
const crumbs = el("div", "picker-crumbs");
|
|
520
|
+
const listBox = el("div", "picker-list");
|
|
521
|
+
const foot = el("div", "picker-foot");
|
|
522
|
+
const relLine = el("div", "note");
|
|
523
|
+
body.append(crumbs, listBox, relLine, foot);
|
|
524
|
+
|
|
525
|
+
let current = null; // the listing payload for the folder on screen
|
|
526
|
+
|
|
527
|
+
const choose = el("button", "btn btn-primary btn-allow-busy", t("picker.choose"));
|
|
528
|
+
choose.type = "button";
|
|
529
|
+
choose.disabled = true;
|
|
530
|
+
|
|
531
|
+
const go = async (path) => {
|
|
532
|
+
listBox.replaceChildren(skeleton(6));
|
|
533
|
+
let d;
|
|
534
|
+
try {
|
|
535
|
+
d = (await read("/api/fs/list" + (path ? "?path=" + encodeURIComponent(path) : ""))).data;
|
|
536
|
+
} catch (e) {
|
|
537
|
+
listBox.replaceChildren(empty(t("picker.unreadable"), String(e.message)));
|
|
538
|
+
return;
|
|
539
|
+
}
|
|
540
|
+
current = d;
|
|
541
|
+
|
|
542
|
+
// Breadcrumbs: the shortcuts you actually want (up, home, this project)
|
|
543
|
+
// rather than a clickable path split on the separator, which on Windows is
|
|
544
|
+
// a row of one-letter targets.
|
|
545
|
+
crumbs.replaceChildren();
|
|
546
|
+
const crumb = (label, target, disabled) => {
|
|
547
|
+
const b = el("button", "btn btn-ghost btn-sm", label);
|
|
548
|
+
b.type = "button";
|
|
549
|
+
b.disabled = !target || disabled;
|
|
550
|
+
b.addEventListener("click", () => go(target));
|
|
551
|
+
return b;
|
|
552
|
+
};
|
|
553
|
+
crumbs.append(crumb("↑ " + t("picker.up"), d.parent));
|
|
554
|
+
crumbs.append(crumb(t("picker.home"), d.home));
|
|
555
|
+
if (d.project_root) crumbs.append(crumb(t("picker.project"), d.project_root));
|
|
556
|
+
crumbs.append(el("code", "picker-path", d.path));
|
|
557
|
+
|
|
558
|
+
listBox.replaceChildren();
|
|
559
|
+
if (d.error) {
|
|
560
|
+
listBox.append(empty(t("picker.unreadable"), d.error));
|
|
561
|
+
} else if (!d.dirs.length) {
|
|
562
|
+
listBox.append(empty(t("picker.empty")));
|
|
563
|
+
} else {
|
|
564
|
+
for (const dir of d.dirs) {
|
|
565
|
+
const row = el("button", "picker-item");
|
|
566
|
+
row.type = "button";
|
|
567
|
+
row.append(el("span", "picker-icon", dir.is_repo ? "◆" : "▸"));
|
|
568
|
+
const mid = el("div");
|
|
569
|
+
mid.append(el("div", "picker-name", dir.name));
|
|
570
|
+
const tags = el("div", "picker-tags");
|
|
571
|
+
// The two facts that decide whether linking this folder is useful.
|
|
572
|
+
if (dir.is_repo) tags.append(chip(t("picker.isRepo"), "info"));
|
|
573
|
+
if (dir.has_wiki) tags.append(chip(t("picker.hasWiki"), "ok"));
|
|
574
|
+
if (d.project_root && dir.path === d.project_root) tags.append(chip(t("picker.sameRepo"), "warn"));
|
|
575
|
+
mid.append(tags);
|
|
576
|
+
row.append(mid, el("span", "picker-into", "→"));
|
|
577
|
+
// Single click NAVIGATES into the folder; "Use this folder" selects the
|
|
578
|
+
// one you are standing in. One gesture per meaning, so a click never
|
|
579
|
+
// both descends and commits.
|
|
580
|
+
row.addEventListener("click", () => go(dir.path));
|
|
581
|
+
listBox.append(row);
|
|
582
|
+
}
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
const isSelf = d.is_project_root;
|
|
586
|
+
choose.disabled = !!d.error || isSelf;
|
|
587
|
+
relLine.textContent = d.error
|
|
588
|
+
? ""
|
|
589
|
+
: isSelf
|
|
590
|
+
? t("picker.sameRepo")
|
|
591
|
+
: t("picker.relative", { rel: d.relative });
|
|
592
|
+
relLine.classList.toggle("picker-warn", isSelf);
|
|
593
|
+
};
|
|
594
|
+
|
|
595
|
+
choose.addEventListener("click", () => {
|
|
596
|
+
if (!current || current.is_project_root) return;
|
|
597
|
+
onPick(current.relative);
|
|
598
|
+
close();
|
|
599
|
+
});
|
|
600
|
+
foot.append(choose);
|
|
601
|
+
|
|
602
|
+
const close = modal({
|
|
603
|
+
title: t("picker.title"),
|
|
604
|
+
body,
|
|
605
|
+
actions: [{ label: t("common.cancel"), onClick: (c) => c() }],
|
|
606
|
+
});
|
|
607
|
+
body.insertBefore(el("div", "note", t("picker.note")), crumbs);
|
|
608
|
+
// Start one level ABOVE the project: a linked repo is almost always a sibling.
|
|
609
|
+
go(metaInfo.project_root ? metaInfo.project_root + "/.." : "");
|
|
610
|
+
return close;
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
function confirmRemove(name, body) {
|
|
614
|
+
const b = el("div");
|
|
615
|
+
b.append(el("div", null, t("crosslink.remove.body", { name })));
|
|
616
|
+
b.append(el("div", "note", t("crosslink.remove.note")));
|
|
617
|
+
b.append(el("div", "action-cmd", `orc crosslink remove ${name}`));
|
|
618
|
+
const close = modal({
|
|
619
|
+
title: t("crosslink.remove.title"),
|
|
620
|
+
body: b,
|
|
621
|
+
actions: [
|
|
622
|
+
{ label: t("common.cancel"), onClick: (c) => c() },
|
|
623
|
+
{
|
|
624
|
+
label: t("common.remove"),
|
|
625
|
+
cls: "btn-danger",
|
|
626
|
+
onClick: async (c) => {
|
|
627
|
+
const r = await post("/api/crosslink/remove", { name });
|
|
628
|
+
toast(r.command, r.ok ? "ok" : "bad", r.output);
|
|
629
|
+
c();
|
|
630
|
+
renderCrosslink(body);
|
|
631
|
+
},
|
|
632
|
+
},
|
|
633
|
+
],
|
|
634
|
+
});
|
|
635
|
+
return close;
|
|
636
|
+
}
|
|
637
|
+
|
|
638
|
+
/* =================================================================== LEARN == */
|
|
639
|
+
|
|
640
|
+
/* ONE THING AT A TIME (v0.43.6).
|
|
641
|
+
This panel used to stack all eight walkthrough sections as eight boxes of
|
|
642
|
+
monospace text, every one of them open. That is the whole document dumped on
|
|
643
|
+
screen: nothing is emphasised, so nothing is read, and finding the section
|
|
644
|
+
you wanted meant scrolling through the seven you did not.
|
|
645
|
+
|
|
646
|
+
A walkthrough has a natural shape — it is ORDERED, and you are at a position
|
|
647
|
+
in it. So the panel now shows a CONTENTS rail and exactly one section, with
|
|
648
|
+
Previous / Next, a progress bar, and a search that filters the rail rather
|
|
649
|
+
than the page. The content is unchanged: it is the same `bin/onboarding-
|
|
650
|
+
content.js` the terminal prints, which is the point — one source, two
|
|
651
|
+
surfaces.
|
|
652
|
+
|
|
653
|
+
Where you are is remembered per browser, so switching panels and coming back
|
|
654
|
+
does not restart the walkthrough. */
|