@corvio/cli 0.1.0-beta.20 → 0.1.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +12 -7
- package/dist/cli.js +182 -25
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,17 +20,22 @@ corvio sync init --dir ./knowledge --root-node-id <node_id> --json
|
|
|
20
20
|
corvio update check --json
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
-
When no environment key is configured, run `corvio auth login`. Login creates one user-level `cvu_` control-plane credential; it does not freeze the browser's current Workspace. Run `corvio agents connect --project .` from the project you want to expose. The command verifies the selected Codex, Claude Code, or Copilot host before server mutation, then idempotently creates or restores the Agent for that provider/device/project binding, grants current/future Workspace coverage under live membership/role and ACL limits, saves the one-time `cvg_` in a `0600` multi-binding registry, and starts that project's listener. `corvio collaboration status` then checks local foreground prerequisites
|
|
23
|
+
When no environment key is configured, run `corvio auth login`. Login creates one user-level `cvu_` control-plane credential; it does not freeze the browser's current Workspace. Run `corvio agents connect --project .` from the project you want to expose. The command verifies the selected Codex, Claude Code, or Copilot host before server mutation, then idempotently creates or restores the Agent for that provider/device/project binding, grants current/future Workspace coverage under live membership/role and ACL limits, saves the one-time `cvg_` in a `0600` multi-binding registry, and starts that project's listener. `corvio collaboration status` then checks local foreground prerequisites and reports package freshness, contract compatibility, installation ownership, every discoverable copy, and the fact that host loading is not observable. A listener receives inbound `@Agent` work; it does not make an unrelated foreground coding session collaborate automatically. Another project gets another Agent. Ordinary data commands prefer the binding whose root contains the current directory, then a locally confirmed selection, the account selection, the personal default, or one authorized candidate. They ask only on real ambiguity. Use `corvio workspaces use`, `--workspace`, and `--agent` as explicit routing/reproducibility controls. Legacy Workspace-bound `cvk_`, `cvg_`, and version-1 credential files remain compatible and are never widened. Local sync stores only credential-free handles and revision/hash baselines in `.corvio/state.json`; Markdown lives below `corvio_docs/`, and conflicts require explicit resolution. Update checks compare npm's published `beta` tag with the API compatibility policy but never self-install. See [the Corvio CLI guide](https://corvio.ai/developers/cli) for the full command and receipt contract.
|
|
24
24
|
|
|
25
|
-
CLI and Skill updates are independent. `corvio update check` checks the CLI package only. `corvio collaboration status` detects a missing
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
25
|
+
CLI and Skill updates are independent. `corvio update check` checks the CLI package only. `corvio collaboration status` detects a missing,
|
|
26
|
+
incompatible, compatible-but-not-current, or conflicting local Skill, but never mutates host directories. Use
|
|
27
|
+
`corvio collaboration status --provider workbuddy --json --no-input` to inspect WorkBuddy's manual copy at
|
|
28
|
+
`~/.workbuddy/skills/corvio-operate-workspace/`; this does not prove which bytes a running WorkBuddy conversation loaded. The canonical
|
|
29
|
+
website ZIP is a static local copy and is not update-tracked by `npx skills update`; reinstall from the same source and scope, then start a
|
|
30
|
+
fresh host session. Until a reviewed WorkBuddy marketplace listing is live, a WorkBuddy upload is an unmanaged manual install and cannot
|
|
31
|
+
auto-update from Corvio. Remote MCP changes are server-delivered and normally need only a refreshed host session unless new OAuth scopes
|
|
32
|
+
require reauthorization.
|
|
30
33
|
|
|
31
34
|
The CLI rejects unknown/duplicate options and malformed integer bounds before network access. Normal API calls have a bounded timeout (`CORVIO_REQUEST_TIMEOUT_MS`, maximum ten minutes); only reads, explicit idempotency contracts, revision guards, and other owner-declared safe operations retry automatically. Document updates preflight the current revision when `--expected-revision` is omitted. `docs table-read` returns a bounded stable-ID projection; `docs table-mutate` accepts a JSON payload for at most 50 cell updates or row appends and requires the fetched revision, while formulas, styles, structure, sorting, and semantic transformations remain `corvio ask --allow-actions` work. When the executing principal is a connected Agent, document and table mutations also require `--change-summary`, create a visible document-level comment after the guarded update, and return its receipt; a comment failure is reported as a partial effect rather than silent success. When a Corvio document or comment supplied the task, use `corvio agent closeout` to return the verified outcome, optionally resolve the thread, and read both document and thread back. Downloads, uploads, and Markdown pulls verify SHA-256 receipts before replacing local files; local writes are atomic, remote default filenames cannot escape the current directory, and Sync refuses symbolic-link traversal while checkpointing each successful push.
|
|
32
35
|
|
|
33
|
-
|
|
36
|
+
Treat files as a value ladder, not one upload event. Upload/finalize preserves the exact original, provenance, hash, policy, and stable Asset ID. A selected Markdown file can become an editable Page through `corvio docs create --file`; for other formats or a coherent source set, use `corvio files organize` or `corvio ask --allow-actions` to produce the reader-facing Page, Spreadsheet, Presentation, Code, HTML Artifact, or reading layer that fits the task. Stable facts/preferences may enter Memory; only an evidence-backed reusable method, configuration, constraint, or quality bar should become a Project Skill.
|
|
37
|
+
|
|
38
|
+
File organization is asynchronous. A queued organization receipt is progress, not completion: continue from its stable operation ID with `corvio files operation <operation_id>`. Blocked operations expose their typed error and can be continued with `corvio files resume <operation_id> --yes`; queued/running/blocked operations can be stopped with `corvio files cancel <operation_id> --yes`. Report completion only after the terminal receipt exposes the output document, source reconciliation, Skill evaluation, and durable links. `skills_extraction_mode=always` requires the evaluation; `evaluated_no_qualifying_skill` is a valid terminal result and is preferable to a fabricated Skill.
|
|
34
39
|
|
|
35
40
|
## Project Agent collaboration
|
|
36
41
|
|
package/dist/cli.js
CHANGED
|
@@ -39,7 +39,7 @@ import {
|
|
|
39
39
|
agentCredentialPath,
|
|
40
40
|
} from "./core.js";
|
|
41
41
|
|
|
42
|
-
const VERSION = "0.1.0-beta.
|
|
42
|
+
const VERSION = "0.1.0-beta.22";
|
|
43
43
|
const DEFAULT_API_BASE = "https://api.corvio.ai/v1";
|
|
44
44
|
const DEFAULT_APP_BASE = "https://corvio.ai";
|
|
45
45
|
const USER_AGENT = `corvio-cli/${VERSION}`;
|
|
@@ -173,16 +173,16 @@ const COMMAND_HELP = Object.freeze({
|
|
|
173
173
|
auth: `Usage: corvio auth login|status|logout\n\nAuthenticate with a user-level device link, inspect the current principal, or revoke the stored credential.`,
|
|
174
174
|
workspaces: `Usage:\n corvio workspaces list\n corvio workspaces use <workspace-id>\n corvio workspaces current\n corvio workspaces create --name <name> [--kind personal|team]\n\nWorkspace status and blocked capability reasons are returned with each item.`,
|
|
175
175
|
capabilities: `Usage: corvio capabilities [--json]\n\nShow live API resources, principal scopes, selected Workspace status, and CLI compatibility.`,
|
|
176
|
-
collaboration: `Usage:\n corvio collaboration status [--project <path>] [--provider codex|claude_code|copilot]\n\nCheck local Corvio prerequisites
|
|
177
|
-
ask: `Usage: corvio ask --prompt <text> [--file <path>] [--asset-ids <id,id>] [--conversation-id <id>] [--sources workspace,web,memory] [--allow-actions] [--processing-profile auto|economy|standard|deep] [--max-processing-profile economy|standard|deep] [--scan-mode auto|always|off] [--skills-extraction-mode auto|always|off] [--caller-model <host-model>] [--context-file <json>] [--idempotency-key <key>]\n\n--file retains the selected local file, verifies its hash, and attaches the finalized Asset to this exact question. --asset-ids attaches already-retained Corvio Assets. Skill extraction mode always requires --allow-actions; answer-only questions may use auto or off.`,
|
|
176
|
+
collaboration: `Usage:\n corvio collaboration status [--project <path>] [--provider codex|claude_code|copilot|workbuddy]\n\nCheck local Corvio prerequisites and every discoverable Corvio Skill copy. Package freshness, contract compatibility, update ownership, and host loading are separate facts. WorkBuddy inspection is read-only and never changes ~/.workbuddy. The CLI cannot prove that the current host task loaded a particular copy, or whether remote MCP or host memory is active.`,
|
|
177
|
+
ask: `Usage: corvio ask --prompt <text> [--file <path>] [--asset-ids <id,id>] [--conversation-id <id>] [--sources workspace,web,memory] [--allow-actions] [--processing-profile auto|economy|standard|deep] [--max-processing-profile economy|standard|deep] [--scan-mode auto|always|off] [--skills-extraction-mode auto|always|off] [--caller-model <host-model>] [--context-file <json>] [--idempotency-key <key>]\n\n--file retains the selected local file, verifies its hash, and attaches the finalized Asset to this exact question. --asset-ids attaches already-retained Corvio Assets. Use --allow-actions when Corvio should create the Page, Spreadsheet, Presentation, Code, HTML Artifact, Memory, or qualifying Project Skill that best serves the result. Poll/read the returned Question artifacts and operations; upload alone is not evidence of interpretation. Skill extraction mode always requires --allow-actions and an evidence-backed decision, not forced Skill creation; answer-only questions may use auto or off.`,
|
|
178
178
|
agents: `Usage:\n corvio agents list\n corvio agents connect [--provider codex|claude_code|copilot] [--project <path>] [--permission-profile read_only|workspace_write|danger_full_access] [--trigger-policy owner_only|workspace_members] [--display-name <name>] [--recover-credential] [--no-listen]\n corvio agents create --handle <handle> --display-name <name> [--provider codex|claude_code|copilot|custom] [--permissions read,comment,edit]\n corvio agents update <id> [--display-name <name>] [--status active|paused|revoked] [--permissions <list>]\n corvio agents rotate <id>\n\n'connect' binds the current Git project (or --project path) to one stable personal Agent. Repeating it is idempotent; another project creates another Agent. It reports provider/CLI, listener, and local Skill prerequisites separately; run corvio collaboration status after installing or refreshing the Skill. The CLI cannot prove that the current host task loaded those surfaces. The default workspace_write profile is limited by the provider's native sandbox. owner_only is the safe trigger default. Secrets and absolute project paths stay local.`,
|
|
179
179
|
agent: `Usage:\n corvio agent claim [--worker-id <id>] [--wait-seconds <0-30>]\n corvio agent renew <event-id> --lease-token <token> [--lease-seconds <30-900>]\n corvio agent complete <event-id> --lease-token <token> [--result-file <json>]\n corvio agent fail <event-id> --lease-token <token> --error <message>\n corvio agent mentions --document-id <id> [--query <name>]\n corvio agent comments --document-id <id>\n corvio agent comment --document-id <id> --body <text> [--selection-file <json>] [--start-new-chain] [--idempotency-key <key>]\n corvio agent reply <thread-id> --document-id <id> --body <text> [--source-event-id <id> --source-event-lease-token <token>|--start-new-chain] [--idempotency-key <key>]\n corvio agent status <thread-id> --document-id <id> --status open|resolved\n corvio agent closeout --document-id <id> [--thread-id <id>] --body <verified-result> [--status open|resolved] [--idempotency-key <key>]\n corvio agent run [--provider codex|claude_code|copilot|custom] [--handler <executable>] [--agent <id>] [--once] [--processing-profile auto|economy|standard|deep] [--max-processing-profile economy|standard|deep] [--scan-mode auto|always|off] [--skills-extraction-mode auto|always|off] [--caller-model <host-model>]\n\nUse mentions before commenting when the target is uncertain; aliases resolve to stable user or Agent IDs and ambiguity fails closed. Use closeout when a Corvio document or comment owned the task: it returns the verified result to that surface and reads document/comments back. Agent-to-Agent comments require either a claimed source event or explicit --start-new-chain. The runner iteratively exchanges origin-scoped evidence with Corvio Query/Writer, then invokes the bound provider inside its local project. Local code changes use the recorded provider permission profile; complex Corvio document changes belong to Query/Writer. Provider children never receive Corvio credentials.`,
|
|
180
180
|
questions: `Usage:\n corvio questions list [--limit <n>] [--cursor <cursor>] [--conversation-id <id>]\n corvio questions get <question-id>`,
|
|
181
181
|
conversations: `Usage:\n corvio conversations list [--limit <n>] [--cursor <cursor>]\n corvio conversations get <conversation-id>`,
|
|
182
182
|
search: `Usage: corvio search <query> [--limit <n>] [--page-id <id>] [--node-id <id>]`,
|
|
183
|
-
docs: `Usage:\n corvio docs list [--limit <n>] [--cursor <cursor>] [--lifecycle active|archived]\n corvio docs get <id> [--output <path>]\n corvio docs table-read <id> [--table-id <id>] [--after-row-id <id>] [--limit <1-100>]\n corvio docs table-mutate <id> --input <json> --yes [--operation-id <id>] [--change-summary <text>]\n corvio docs create --title <title> [--file <path>|--markdown <text>] [--parent-node-id <id>] [--idempotency-key <key>]\n corvio docs update <id> [--title <title>] [--file <path>|--markdown <text>] [--expected-revision <n>] [--change-summary <text>]\n corvio docs move <id> --parent-node-id <id>\n corvio docs archive <id> --yes\n corvio docs restore <id> --yes\n corvio docs share <id> --yes [--display-name <name>]\n corvio docs open <id>\n\
|
|
183
|
+
docs: `Usage:\n corvio docs list [--limit <n>] [--cursor <cursor>] [--lifecycle active|archived]\n corvio docs get <id> [--output <path>]\n corvio docs table-read <id> [--table-id <id>] [--after-row-id <id>] [--limit <1-100>]\n corvio docs table-mutate <id> --input <json> --yes [--operation-id <id>] [--change-summary <text>]\n corvio docs create --title <title> [--file <path>|--markdown <text>] [--parent-node-id <id>] [--idempotency-key <key>]\n corvio docs update <id> [--title <title>] [--file <path>|--markdown <text>] [--expected-revision <n>] [--change-summary <text>]\n corvio docs move <id> --parent-node-id <id>\n corvio docs archive <id> --yes\n corvio docs restore <id> --yes\n corvio docs share <id> --yes [--display-name <name>]\n corvio docs open <id>\n\nA selected Markdown file is uploaded once as a provenance-preserving Asset, then its source_asset_id becomes an editable Page without resending the body; inspect the returned document ID, node ID, revision, and URL. Other formats or semantic restructuring belong to \`corvio files organize\` or \`corvio ask --allow-actions\`. Use table-read plus table-mutate for bounded stable-ID cell updates or row appends without round-tripping a whole document. Agent-authenticated document changes require --change-summary and create a visible document-level comment after the guarded update. When a document/comment is the task owner, use \`corvio agent closeout\` to return verified work to that same collaboration surface and read it back.`,
|
|
184
184
|
projects: `Usage:\n corvio projects list [--limit <n>]\n corvio projects create --title <title> [--idempotency-key <key>]\n\nProjects are durable grouping owners in the Corvio Docs tree. Reuse a matching Project instead of creating one ceremonial Project per file. 'corvio folders' is an alias.`,
|
|
185
|
-
files: `Usage:\n corvio files list [--limit <n>]\n corvio files get <id>\n corvio files download <id> [--output <path>]\n corvio files upload --file <path> [--mime-type <type>] [--organize --instruction <text> --additional-asset-ids <id,id> --yes]\n corvio files organize <id> --instruction <text> --yes [--additional-asset-ids <id,id>] [--processing-profile auto|economy|standard|deep] [--scan-mode auto|always|off] [--skills-extraction-mode auto|always|off]\n corvio files operation <operation-id>\n corvio files resume <operation-id> --yes\n corvio files cancel <operation-id> --yes\n corvio files open <id>\n\nUpload alone preserves the original
|
|
185
|
+
files: `Usage:\n corvio files list [--limit <n>]\n corvio files get <id>\n corvio files download <id> [--output <path>]\n corvio files upload --file <path> [--mime-type <type>] [--organize --instruction <text> --additional-asset-ids <id,id> --yes]\n corvio files organize <id> --instruction <text> --yes [--additional-asset-ids <id,id>] [--processing-profile auto|economy|standard|deep] [--scan-mode auto|always|off] [--skills-extraction-mode auto|always|off]\n corvio files operation <operation-id>\n corvio files resume <operation-id> --yes\n corvio files cancel <operation-id> --yes\n corvio files open <id>\n\nUpload alone preserves the exact original, provenance, hash, policy, and stable Asset ID. Organization is a separate asynchronous value step: one coherent source set can produce reader-facing structure, source reconciliation, Memory candidates, and qualifying Project Skills. Use 'files operation' to reach terminal and inspect output_document plus skills_evaluation; always requires evaluation but may correctly return evaluated_no_qualifying_skill. Resume reuses the same durable operation after resolving its blocker; cancel is idempotent.`,
|
|
186
186
|
sync: `Usage:\n corvio sync init --root-node-id <id> [--dir <path>] [--name <name>]\n corvio sync status|plan|pull [--dir <path>]\n corvio sync push --yes [--dir <path>]\n corvio sync resolve --conflict-id <id> --strategy use-remote|keep-local --yes [--dir <path>]`,
|
|
187
187
|
update: `Usage: corvio update check [--json]\n\nCompare this executable with the reviewed API policy and the npm beta dist-tag.`,
|
|
188
188
|
});
|
|
@@ -724,6 +724,7 @@ async function codexPluginGuidanceCandidates(codexHome) {
|
|
|
724
724
|
for (const version of await listDirectories(pluginRoot)) {
|
|
725
725
|
candidates.push({
|
|
726
726
|
scope: "codex_plugin_cache",
|
|
727
|
+
priority: 100,
|
|
727
728
|
path: join(pluginRoot, version, "skills", "corvio-operate-workspace", "SKILL.md"),
|
|
728
729
|
});
|
|
729
730
|
}
|
|
@@ -732,24 +733,103 @@ async function codexPluginGuidanceCandidates(codexHome) {
|
|
|
732
733
|
return candidates;
|
|
733
734
|
}
|
|
734
735
|
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
736
|
+
function collaborationRevision(source) {
|
|
737
|
+
return source.match(/contract (?:version|revision) `([0-9]{4}-[0-9]{2}-[0-9]{2}\.[0-9]+)`/)?.[1] || null;
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
function collaborationRelease(source) {
|
|
741
|
+
return source.match(/Skill release `([0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?)`/)?.[1] || null;
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
function collaborationCompatibilityFamily(source) {
|
|
745
|
+
return source.match(/compatibility family\s+`([^`]+)`/)?.[1] || null;
|
|
746
|
+
}
|
|
747
|
+
|
|
748
|
+
function collaborationRevisionAtLeast(actual, minimum) {
|
|
749
|
+
const parse = (value) => {
|
|
750
|
+
const match = String(value || "").match(/^(\d{4})-(\d{2})-(\d{2})\.(\d+)$/);
|
|
751
|
+
return match ? [Number(match[1]), Number(match[2]), Number(match[3]), Number(match[4])] : null;
|
|
752
|
+
};
|
|
753
|
+
const left = parse(actual);
|
|
754
|
+
const right = parse(minimum);
|
|
755
|
+
if (!left || !right) return false;
|
|
756
|
+
for (let index = 0; index < left.length; index += 1) {
|
|
757
|
+
if (left[index] !== right[index]) return left[index] > right[index];
|
|
758
|
+
}
|
|
759
|
+
return true;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
async function loadOfficialSkillManifest(liveContract) {
|
|
763
|
+
const manifestUrl = String(dictLike(dictLike(liveContract?.distribution).official_skill).manifest || "").trim();
|
|
764
|
+
if (!manifestUrl) return { status: "unavailable", manifest: {} };
|
|
765
|
+
let url;
|
|
766
|
+
try {
|
|
767
|
+
url = new URL(manifestUrl);
|
|
768
|
+
} catch {
|
|
769
|
+
return { status: "invalid_url", manifest: {} };
|
|
770
|
+
}
|
|
771
|
+
if (!new Set(["https:", "http:"]).has(url.protocol)) return { status: "invalid_url", manifest: {} };
|
|
772
|
+
try {
|
|
773
|
+
const response = await fetch(url, {
|
|
774
|
+
headers: { accept: "application/json", "user-agent": USER_AGENT },
|
|
775
|
+
signal: AbortSignal.timeout(5_000),
|
|
776
|
+
});
|
|
777
|
+
if (!response.ok) return { status: `http_${response.status}`, manifest: {} };
|
|
778
|
+
return { status: "available", manifest: dictLike(await response.json()) };
|
|
779
|
+
} catch {
|
|
780
|
+
return { status: "unavailable", manifest: {} };
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
|
|
784
|
+
async function inspectCollaborationGuidance(provider, projectRoot, liveContract, manifestReceipt = {}) {
|
|
785
|
+
const distribution = dictLike(liveContract?.distribution);
|
|
786
|
+
const requiresContentHash = Number(distribution.schema_version || 0) >= 2;
|
|
787
|
+
const compatibility = dictLike(distribution.compatibility);
|
|
788
|
+
const officialSkill = dictLike(distribution.official_skill);
|
|
789
|
+
const expectedContractVersion = String(liveContract?.version || compatibility.current_revision || "").trim() || null;
|
|
790
|
+
const expectedReleaseVersion = String(officialSkill.current_release || "").trim() || null;
|
|
791
|
+
const manifest = dictLike(manifestReceipt.manifest);
|
|
792
|
+
const expectedSkillSha256 = Array.isArray(manifest.files)
|
|
793
|
+
? String(manifest.files.find((file) => file?.path === "SKILL.md")?.sha256 || "").trim() || null
|
|
794
|
+
: null;
|
|
795
|
+
const expectedFamily = String(compatibility.family || "").trim() || null;
|
|
796
|
+
const minimumRevision = String(compatibility.minimum_supported_skill_revision || "").trim()
|
|
797
|
+
|| expectedContractVersion;
|
|
798
|
+
const legacyFamilylessRevisions = Array.isArray(compatibility.legacy_familyless_revisions)
|
|
799
|
+
? compatibility.legacy_familyless_revisions.map((value) => String(value || "").trim()).filter(Boolean)
|
|
800
|
+
: [];
|
|
801
|
+
const candidates = [];
|
|
802
|
+
if (provider === "workbuddy") {
|
|
803
|
+
const workbuddyHome = String(process.env.WORKBUDDY_HOME || join(homedir(), ".workbuddy"));
|
|
804
|
+
candidates.push({
|
|
805
|
+
scope: "user_workbuddy",
|
|
806
|
+
source: "manual_host_copy",
|
|
807
|
+
priority: 200,
|
|
808
|
+
path: join(workbuddyHome, "skills", "corvio-operate-workspace", "SKILL.md"),
|
|
809
|
+
});
|
|
810
|
+
} else {
|
|
811
|
+
candidates.push(
|
|
812
|
+
{ scope: "project_agents", source: "project_skill", priority: 300, path: join(projectRoot, ".agents", "skills", "corvio-operate-workspace", "SKILL.md") },
|
|
813
|
+
{ scope: "user_agents", source: "user_skill", priority: 200, path: join(homedir(), ".agents", "skills", "corvio-operate-workspace", "SKILL.md") },
|
|
814
|
+
);
|
|
815
|
+
}
|
|
740
816
|
if (provider === "codex") {
|
|
741
817
|
const codexHome = String(process.env.CODEX_HOME || join(homedir(), ".codex"));
|
|
742
818
|
candidates.push({
|
|
743
819
|
scope: "user_codex",
|
|
820
|
+
source: "user_skill",
|
|
821
|
+
priority: 200,
|
|
744
822
|
path: join(codexHome, "skills", "corvio-operate-workspace", "SKILL.md"),
|
|
745
823
|
});
|
|
746
824
|
candidates.push(...await codexPluginGuidanceCandidates(codexHome));
|
|
747
825
|
}
|
|
748
826
|
if (provider === "claude_code") {
|
|
749
827
|
candidates.push(
|
|
750
|
-
{ scope: "project_claude", path: join(projectRoot, ".claude", "skills", "corvio-operate-workspace", "SKILL.md") },
|
|
828
|
+
{ scope: "project_claude", source: "project_skill", priority: 300, path: join(projectRoot, ".claude", "skills", "corvio-operate-workspace", "SKILL.md") },
|
|
751
829
|
{
|
|
752
830
|
scope: "user_claude",
|
|
831
|
+
source: "user_skill",
|
|
832
|
+
priority: 200,
|
|
753
833
|
path: join(String(process.env.CLAUDE_CONFIG_DIR || join(homedir(), ".claude")), "skills", "corvio-operate-workspace", "SKILL.md"),
|
|
754
834
|
},
|
|
755
835
|
);
|
|
@@ -759,29 +839,98 @@ async function inspectCollaborationGuidance(provider, projectRoot, expectedContr
|
|
|
759
839
|
try {
|
|
760
840
|
const source = await readFile(candidate.path, "utf8");
|
|
761
841
|
if (!/^name:\s*corvio-operate-workspace\s*$/m.test(source)) continue;
|
|
842
|
+
const contractRevision = collaborationRevision(source);
|
|
843
|
+
const releaseVersion = collaborationRelease(source);
|
|
844
|
+
const family = collaborationCompatibilityFamily(source);
|
|
845
|
+
const contentSha256 = createHash("sha256").update(source).digest("hex");
|
|
846
|
+
const exactRevision = Boolean(expectedContractVersion) && contractRevision === expectedContractVersion;
|
|
847
|
+
const exactRelease = Boolean(expectedReleaseVersion) && releaseVersion === expectedReleaseVersion;
|
|
848
|
+
const exactContent = Boolean(expectedSkillSha256) && contentSha256 === expectedSkillSha256;
|
|
849
|
+
const familyMatches = family === expectedFamily
|
|
850
|
+
|| (!family && legacyFamilylessRevisions.includes(contractRevision));
|
|
851
|
+
const compatible = expectedFamily
|
|
852
|
+
? familyMatches && collaborationRevisionAtLeast(contractRevision, minimumRevision)
|
|
853
|
+
: exactRevision;
|
|
854
|
+
const declaredCurrent = compatible && (
|
|
855
|
+
expectedReleaseVersion ? exactRelease : exactRevision
|
|
856
|
+
);
|
|
762
857
|
found.push({
|
|
763
858
|
scope: candidate.scope,
|
|
764
|
-
|
|
765
|
-
|
|
859
|
+
source: candidate.source || "plugin_cache",
|
|
860
|
+
priority: candidate.priority || 100,
|
|
861
|
+
path: candidate.path,
|
|
862
|
+
release_version: releaseVersion,
|
|
863
|
+
contract_revision: contractRevision,
|
|
864
|
+
compatibility_family: family,
|
|
865
|
+
content_sha256: contentSha256,
|
|
866
|
+
current: expectedSkillSha256
|
|
867
|
+
? exactContent && compatible
|
|
868
|
+
: !requiresContentHash && declaredCurrent,
|
|
869
|
+
declared_current: declaredCurrent,
|
|
870
|
+
integrity_match: expectedSkillSha256 ? exactContent : null,
|
|
871
|
+
compatible,
|
|
766
872
|
});
|
|
767
873
|
} catch (error) {
|
|
768
874
|
if (error?.code !== "ENOENT") throw error;
|
|
769
875
|
}
|
|
770
876
|
}
|
|
771
|
-
const
|
|
772
|
-
const
|
|
877
|
+
const effectivePriority = found.length ? Math.max(...found.map((item) => item.priority)) : null;
|
|
878
|
+
const evaluated = effectivePriority === null ? [] : found.filter((item) => item.priority === effectivePriority);
|
|
879
|
+
const current = evaluated.filter((item) => item.current);
|
|
880
|
+
const compatible = evaluated.filter((item) => item.compatible);
|
|
881
|
+
const distinctCopies = new Set(evaluated.map((item) => item.content_sha256));
|
|
882
|
+
const integrityMismatch = Boolean(expectedSkillSha256) && evaluated.some(
|
|
883
|
+
(item) => item.release_version === expectedReleaseVersion && item.content_sha256 !== expectedSkillSha256,
|
|
884
|
+
);
|
|
885
|
+
let packageStatus = "update_required";
|
|
886
|
+
if (!expectedContractVersion) packageStatus = "contract_unavailable";
|
|
887
|
+
else if (!evaluated.length) packageStatus = "missing";
|
|
888
|
+
else if (distinctCopies.size > 1) packageStatus = "multiple_copies_conflict";
|
|
889
|
+
else if (integrityMismatch) packageStatus = "update_required";
|
|
890
|
+
else if (current.length) packageStatus = "current";
|
|
891
|
+
else if (compatible.length && !expectedSkillSha256 && evaluated.some((item) => item.declared_current)) {
|
|
892
|
+
packageStatus = "freshness_unverified";
|
|
893
|
+
} else if (compatible.length) packageStatus = "compatible_update_available";
|
|
894
|
+
const status = packageStatus === "contract_unavailable"
|
|
773
895
|
? "contract_unavailable"
|
|
774
|
-
:
|
|
775
|
-
? "
|
|
776
|
-
:
|
|
777
|
-
? "
|
|
778
|
-
: "
|
|
896
|
+
: packageStatus === "missing"
|
|
897
|
+
? "missing"
|
|
898
|
+
: ["current", "compatible_update_available", "freshness_unverified"].includes(packageStatus)
|
|
899
|
+
? "ready"
|
|
900
|
+
: "stale";
|
|
901
|
+
const managementStatus = provider === "workbuddy" && found.length
|
|
902
|
+
? "unmanaged_manual_install"
|
|
903
|
+
: "host_or_installer_managed_unknown";
|
|
904
|
+
const workbuddyAction = dictLike(dictLike(distribution.host_channels).workbuddy_manual_skill).action;
|
|
905
|
+
const refreshAvailable = new Set([
|
|
906
|
+
"missing", "compatible_update_available", "update_required", "multiple_copies_conflict",
|
|
907
|
+
]).has(packageStatus);
|
|
779
908
|
return {
|
|
780
909
|
status,
|
|
910
|
+
package_status: packageStatus,
|
|
911
|
+
management_status: managementStatus,
|
|
912
|
+
host_loading_status: "host_loading_unverified",
|
|
781
913
|
expected_contract_version: expectedContractVersion || null,
|
|
914
|
+
expected_release_version: expectedReleaseVersion,
|
|
915
|
+
expected_skill_md_sha256: expectedSkillSha256,
|
|
916
|
+
upstream_manifest_status: manifestReceipt.status || "unavailable",
|
|
917
|
+
compatibility: {
|
|
918
|
+
family: expectedFamily,
|
|
919
|
+
minimum_supported_revision: minimumRevision,
|
|
920
|
+
legacy_familyless_revisions: legacyFamilylessRevisions,
|
|
921
|
+
},
|
|
782
922
|
discovered_scopes: found.map((item) => item.scope),
|
|
923
|
+
effective_scopes: evaluated.map((item) => item.scope),
|
|
924
|
+
shadowed_scopes: found.filter((item) => item.priority !== effectivePriority).map((item) => item.scope),
|
|
783
925
|
current_scopes: current.map((item) => item.scope),
|
|
784
|
-
|
|
926
|
+
compatible_scopes: compatible.map((item) => item.scope),
|
|
927
|
+
discovered_copies: found,
|
|
928
|
+
install_command: provider !== "workbuddy" && refreshAvailable
|
|
929
|
+
? CORVIO_SKILL_INSTALL_COMMAND
|
|
930
|
+
: null,
|
|
931
|
+
update_action: provider === "workbuddy" && refreshAvailable
|
|
932
|
+
? workbuddyAction || "Download the official Skill ZIP, replace it in WorkBuddy, then start a fresh conversation."
|
|
933
|
+
: null,
|
|
785
934
|
};
|
|
786
935
|
}
|
|
787
936
|
|
|
@@ -794,23 +943,31 @@ async function collaborationStatus(options) {
|
|
|
794
943
|
fallbackToActive: false,
|
|
795
944
|
});
|
|
796
945
|
const provider = String(options.provider || credential?.metadata?.provider || "codex");
|
|
946
|
+
if (!["codex", "claude_code", "copilot", "workbuddy"].includes(provider)) {
|
|
947
|
+
throw new CliError("--provider must be codex, claude_code, copilot, or workbuddy.", {
|
|
948
|
+
code: "invalid_provider",
|
|
949
|
+
});
|
|
950
|
+
}
|
|
797
951
|
const api = new ApiClient({ baseUrl: baseUrl(options), userAgent: USER_AGENT });
|
|
798
952
|
const index = await api.request("", { auth: false });
|
|
799
953
|
const liveContract = dictLike(index?.coding_agent_collaboration);
|
|
800
954
|
const serverVersion = String(liveContract.version || "").trim() || null;
|
|
801
|
-
const
|
|
955
|
+
const manifestReceipt = await loadOfficialSkillManifest(liveContract);
|
|
956
|
+
const guidance = await inspectCollaborationGuidance(provider, projectRoot, liveContract, manifestReceipt);
|
|
802
957
|
const bindingId = String(credential?.metadata?.binding_id || "").trim();
|
|
803
958
|
const runnerState = await readAgentRunnerState();
|
|
804
959
|
const listener = bindingId ? dictLike(runnerState?.listeners?.[bindingId]) : {};
|
|
805
960
|
const listenerStatus = processIsRunning(Number(listener.pid)) ? "listening" : "offline";
|
|
961
|
+
const requiresProjectAgent = provider !== "workbuddy";
|
|
806
962
|
let status = "ready";
|
|
807
|
-
if (!credential) status = "needs_connection";
|
|
963
|
+
if (!credential && requiresProjectAgent) status = "needs_connection";
|
|
808
964
|
else if (!serverVersion) status = "contract_unavailable";
|
|
809
965
|
else if (guidance.status === "missing") status = "needs_skill";
|
|
810
966
|
else if (guidance.status === "stale") status = "stale_skill";
|
|
811
967
|
const nextActions = [];
|
|
812
|
-
if (!credential) nextActions.push(`corvio agents connect --provider ${provider} --project .`);
|
|
968
|
+
if (!credential && requiresProjectAgent) nextActions.push(`corvio agents connect --provider ${provider} --project .`);
|
|
813
969
|
if (guidance.install_command) nextActions.push(guidance.install_command);
|
|
970
|
+
if (guidance.update_action) nextActions.push(guidance.update_action);
|
|
814
971
|
if (!serverVersion) nextActions.push("corvio update check --json");
|
|
815
972
|
const localPrerequisitesReady = status === "ready";
|
|
816
973
|
const hostIntegration = dictLike(liveContract.host_integration);
|
|
@@ -1509,7 +1666,7 @@ async function agents(options, action, id) {
|
|
|
1509
1666
|
const hostGuidance = await inspectCollaborationGuidance(
|
|
1510
1667
|
provider,
|
|
1511
1668
|
binding.project_root,
|
|
1512
|
-
collaborationVersion,
|
|
1669
|
+
{ version: collaborationVersion },
|
|
1513
1670
|
);
|
|
1514
1671
|
result = {
|
|
1515
1672
|
connected: true,
|