@indigoai-us/hq-cli 5.77.14 → 5.78.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 +23 -0
- package/assets/scaffold/core/scripts/archive-old-threads.sh +86 -0
- package/assets/scaffold/core/scripts/backfill-company-skill-mirrors.sh +73 -0
- package/assets/scaffold/core/scripts/backfill-workspace-mirror.sh +51 -0
- package/assets/scaffold/core/scripts/codex-skill-bridge.sh +550 -0
- package/assets/scaffold/core/scripts/detect-stale-core-policy-mirror.sh +185 -0
- package/assets/scaffold/core/scripts/hq-status-summary.sh +187 -0
- package/assets/scaffold/core/scripts/ontology-readme-drift.sh +264 -0
- package/assets/scaffold/core/scripts/qmd-reindex-after-sync.sh +93 -0
- package/assets/scaffold/core/scripts/rebuild-all-indexes.sh +69 -0
- package/assets/scaffold/core/scripts/rebuild-companies-index.sh +206 -0
- package/assets/scaffold/core/scripts/rebuild-company-knowledge-index.sh +127 -0
- package/assets/scaffold/core/scripts/rebuild-orchestrator-index.sh +86 -0
- package/assets/scaffold/core/scripts/rebuild-projects-index.sh +105 -0
- package/assets/scaffold/core/scripts/rebuild-public-knowledge-index.sh +83 -0
- package/assets/scaffold/core/scripts/rebuild-reports-index.sh +114 -0
- package/assets/scaffold/core/scripts/rebuild-social-drafts-index.sh +135 -0
- package/assets/scaffold/core/scripts/rebuild-threads-index.sh +82 -0
- package/assets/scaffold/core/scripts/rebuild-workers-index.sh +97 -0
- package/assets/scaffold/core/scripts/resize-screenshot.sh +47 -0
- package/assets/scaffold/core/scripts/token-usage-report.sh +254 -0
- package/assets/scaffold/core/scripts/worktree.sh +136 -0
- package/dist/commands/agents.js +28 -12
- package/dist/commands/api-keys.js +100 -20
- package/dist/commands/core.d.ts +90 -0
- package/dist/commands/core.js +279 -0
- package/dist/commands/files-browse.d.ts +21 -0
- package/dist/commands/files-browse.js +145 -24
- package/dist/commands/files.js +33 -7
- package/dist/commands/members.js +16 -5
- package/dist/commands/pack-install.d.ts +14 -0
- package/dist/commands/pack-install.js +42 -2
- package/dist/commands/pkg-install.js +39 -2
- package/dist/commands/run.js +6 -2
- package/dist/commands/secrets.js +120 -65
- package/dist/main.js +6 -0
- package/dist/utils/api-key-command-gate.d.ts +45 -0
- package/dist/utils/api-key-command-gate.js +51 -0
- package/dist/utils/hq-roots.d.ts +91 -0
- package/dist/utils/hq-roots.js +212 -0
- package/dist/utils/resolve-vault-credential.d.ts +63 -3
- package/dist/utils/resolve-vault-credential.js +152 -6
- package/dist/utils/run-bundled-script.d.ts +66 -0
- package/dist/utils/run-bundled-script.js +80 -0
- package/dist/utils/vault-api.d.ts +21 -0
- package/dist/utils/vault-api.js +74 -3
- package/package.json +7 -1
- package/.claude/policies/hq-cli-interactive-reads-use-sts-vend.md +0 -49
- package/.github/workflows/ci.yml +0 -46
- package/.github/workflows/publish.yml +0 -36
- package/.github/workflows/scripts/smoke-test-pkg.sh +0 -97
- package/e2e/cli.test.ts +0 -128
- package/eslint.config.js +0 -37
- package/pnpm-workspace.yaml +0 -4
- package/scripts/chmod-bins.mjs +0 -49
- package/scripts/generate-dsn.mjs +0 -26
- package/scripts/generate-scan-packages-table.mjs +0 -113
- package/scripts/smoke-sources-signals.sh +0 -103
- package/src/bin/hq-auth-refresh.ts +0 -43
- package/src/cli-version.ts +0 -13
- package/src/commands/__fixtures__/make-tar.ts +0 -126
- package/src/commands/_patterns.ts +0 -10
- package/src/commands/add.ts +0 -75
- package/src/commands/agents.test.ts +0 -527
- package/src/commands/agents.ts +0 -923
- package/src/commands/api-keys.test.ts +0 -291
- package/src/commands/api-keys.ts +0 -382
- package/src/commands/artifact-verify.test.ts +0 -177
- package/src/commands/auth.test.ts +0 -82
- package/src/commands/auth.ts +0 -213
- package/src/commands/billing.test.ts +0 -158
- package/src/commands/billing.ts +0 -146
- package/src/commands/channels.test.ts +0 -74
- package/src/commands/channels.ts +0 -91
- package/src/commands/cloud-demote.test.ts +0 -401
- package/src/commands/cloud-demote.ts +0 -278
- package/src/commands/cloud-provision.test.ts +0 -1117
- package/src/commands/cloud-provision.ts +0 -942
- package/src/commands/cloud.pull-all.test.ts +0 -711
- package/src/commands/cloud.pull-per-company.test.ts +0 -188
- package/src/commands/cloud.push-all.test.ts +0 -485
- package/src/commands/cloud.scope-excluded-warning.test.ts +0 -22
- package/src/commands/cloud.selectors.test.ts +0 -208
- package/src/commands/cloud.status.test.ts +0 -160
- package/src/commands/cloud.test.ts +0 -41
- package/src/commands/cloud.ts +0 -2130
- package/src/commands/company.test.ts +0 -202
- package/src/commands/company.ts +0 -148
- package/src/commands/creators.test.ts +0 -60
- package/src/commands/creators.ts +0 -117
- package/src/commands/crm.test.ts +0 -179
- package/src/commands/crm.ts +0 -236
- package/src/commands/db-migrate.ts +0 -55
- package/src/commands/db-provision.ts +0 -114
- package/src/commands/db-sql.ts +0 -124
- package/src/commands/db-status.ts +0 -108
- package/src/commands/db.ts +0 -26
- package/src/commands/dm.test.ts +0 -748
- package/src/commands/dm.ts +0 -1226
- package/src/commands/feedback.test.ts +0 -413
- package/src/commands/feedback.ts +0 -169
- package/src/commands/files-browse.test.ts +0 -1249
- package/src/commands/files-browse.ts +0 -1506
- package/src/commands/files-delete.test.ts +0 -484
- package/src/commands/files-recovery.test.ts +0 -370
- package/src/commands/files.test.ts +0 -722
- package/src/commands/files.ts +0 -1446
- package/src/commands/group-grants.test.ts +0 -330
- package/src/commands/group-grants.ts +0 -455
- package/src/commands/groups.test.ts +0 -44
- package/src/commands/groups.ts +0 -368
- package/src/commands/integrations.test.ts +0 -633
- package/src/commands/integrations.ts +0 -507
- package/src/commands/list.ts +0 -85
- package/src/commands/login.ts +0 -52
- package/src/commands/logout.ts +0 -30
- package/src/commands/marketplace-install.test.ts +0 -543
- package/src/commands/marketplace-security.test.ts +0 -646
- package/src/commands/mcp-registration.test.ts +0 -2787
- package/src/commands/mcp-registration.ts +0 -2612
- package/src/commands/mcp-status.test.ts +0 -483
- package/src/commands/mcp-status.ts +0 -575
- package/src/commands/mcp-status.us011.test.ts +0 -243
- package/src/commands/meetings.test.ts +0 -522
- package/src/commands/meetings.ts +0 -789
- package/src/commands/members.test.ts +0 -1474
- package/src/commands/members.ts +0 -1181
- package/src/commands/onboard-identity-guard.test.ts +0 -99
- package/src/commands/onboard-identity-guard.ts +0 -103
- package/src/commands/onboard-join.test.ts +0 -96
- package/src/commands/onboard-join.ts +0 -101
- package/src/commands/onboard-warning.test.ts +0 -26
- package/src/commands/onboard-warning.ts +0 -12
- package/src/commands/onboard.ts +0 -267
- package/src/commands/outposts-heartbeat.test.ts +0 -299
- package/src/commands/outposts-heartbeat.ts +0 -310
- package/src/commands/outposts-replica-sync.test.ts +0 -272
- package/src/commands/outposts-self-deploy.test.ts +0 -243
- package/src/commands/outposts.test.ts +0 -1039
- package/src/commands/outposts.ts +0 -1935
- package/src/commands/pack-install-secret-authorization.test.ts +0 -115
- package/src/commands/pack-install.test.ts +0 -1469
- package/src/commands/pack-install.ts +0 -2256
- package/src/commands/pack-update-cache.test.ts +0 -149
- package/src/commands/packs-update-api-key.test.ts +0 -105
- package/src/commands/packs.test.ts +0 -88
- package/src/commands/packs.ts +0 -622
- package/src/commands/people.test.ts +0 -807
- package/src/commands/people.ts +0 -422
- package/src/commands/pkg-install.dispatch.test.ts +0 -103
- package/src/commands/pkg-install.ts +0 -221
- package/src/commands/pkg-list.test.ts +0 -79
- package/src/commands/pkg-list.ts +0 -13
- package/src/commands/pkg-remove.ts +0 -71
- package/src/commands/pkg-update.ts +0 -189
- package/src/commands/publish.test.ts +0 -650
- package/src/commands/publish.ts +0 -580
- package/src/commands/reindex.test.ts +0 -259
- package/src/commands/reindex.ts +0 -274
- package/src/commands/rescue.test.ts +0 -39
- package/src/commands/rescue.ts +0 -210
- package/src/commands/run.env-local.test.ts +0 -88
- package/src/commands/run.test.ts +0 -45
- package/src/commands/run.ts +0 -190
- package/src/commands/safe-extract.test.ts +0 -459
- package/src/commands/safe-extract.ts +0 -444
- package/src/commands/secrets-scope.test.ts +0 -56
- package/src/commands/secrets-scope.ts +0 -32
- package/src/commands/secrets.parse-destination.test.ts +0 -38
- package/src/commands/secrets.test.ts +0 -2050
- package/src/commands/secrets.ts +0 -2249
- package/src/commands/signals.ts +0 -351
- package/src/commands/skill.test.ts +0 -770
- package/src/commands/skill.ts +0 -796
- package/src/commands/sources.ts +0 -362
- package/src/commands/sync-mode.test.ts +0 -366
- package/src/commands/sync-mode.ts +0 -387
- package/src/commands/sync-narrow.test.ts +0 -633
- package/src/commands/sync-narrow.ts +0 -555
- package/src/commands/sync.ts +0 -165
- package/src/commands/team-sync.ts +0 -628
- package/src/commands/update.ts +0 -71
- package/src/commands/whoami.ts +0 -83
- package/src/commands/workers.test.ts +0 -158
- package/src/commands/workers.ts +0 -301
- package/src/index.test.ts +0 -32
- package/src/index.ts +0 -20
- package/src/lib/db/control-plane.test.ts +0 -59
- package/src/lib/db/control-plane.ts +0 -113
- package/src/lib/db/local.test.ts +0 -81
- package/src/lib/db/local.ts +0 -148
- package/src/lib/db/migrate.test.ts +0 -133
- package/src/lib/db/migrate.ts +0 -137
- package/src/lib/db/paths.test.ts +0 -112
- package/src/lib/db/paths.ts +0 -128
- package/src/lib/db/remote-engine.test.ts +0 -44
- package/src/lib/db/remote-engine.ts +0 -148
- package/src/lib/db/remote-sql.test.ts +0 -32
- package/src/lib/db/remote-sql.ts +0 -62
- package/src/lib/db/sql.test.ts +0 -106
- package/src/lib/db/sql.ts +0 -192
- package/src/lib/hq-cloud-dep.smoke.test.ts +0 -75
- package/src/lib/local-tree-diff.test.ts +0 -301
- package/src/lib/local-tree-diff.ts +0 -362
- package/src/lib/narrow-hint-banner.test.ts +0 -235
- package/src/lib/narrow-hint-banner.ts +0 -212
- package/src/lib/plan-limit-nag.test.ts +0 -317
- package/src/lib/plan-limit-nag.ts +0 -264
- package/src/main.ts +0 -371
- package/src/node-preflight.test.ts +0 -60
- package/src/node-preflight.ts +0 -67
- package/src/outpost/session-heartbeat-bounds.test.ts +0 -195
- package/src/outpost/session-heartbeat-guard.test.ts +0 -105
- package/src/outpost/session-heartbeat-publisher.test.ts +0 -178
- package/src/outpost/session-heartbeat-publisher.ts +0 -186
- package/src/outpost/session-heartbeat-retain-guard.test.ts +0 -126
- package/src/outpost/session-heartbeat.test.ts +0 -459
- package/src/outpost/session-heartbeat.ts +0 -877
- package/src/packaging.test.ts +0 -109
- package/src/run/__fixtures__/discover-schemas/example.env.schema +0 -4
- package/src/run/discover-schemas.test.ts +0 -153
- package/src/run/discover-schemas.ts +0 -79
- package/src/run/hq-plugin.test.ts +0 -371
- package/src/run/hq-plugin.ts +0 -277
- package/src/run/varlock-shape.test.ts +0 -57
- package/src/schemas/hq-package.schema.json +0 -155
- package/src/sentry-before-send.test.ts +0 -359
- package/src/sentry-before-send.ts +0 -221
- package/src/sentry-epipe.test.ts +0 -37
- package/src/sentry-release.test.ts +0 -54
- package/src/sentry.test.ts +0 -24
- package/src/sentry.ts +0 -54
- package/src/strategies/link.ts +0 -62
- package/src/strategies/merge.ts +0 -142
- package/src/types.ts +0 -146
- package/src/utils/__fixtures__/scan-packages.generated-block.sh +0 -23
- package/src/utils/auth-error.test.ts +0 -40
- package/src/utils/auth-error.ts +0 -42
- package/src/utils/billing-gate.test.ts +0 -95
- package/src/utils/billing-gate.ts +0 -228
- package/src/utils/breadcrumb-buffer.ts +0 -18
- package/src/utils/cli-telemetry.test.ts +0 -153
- package/src/utils/cli-telemetry.ts +0 -61
- package/src/utils/cognito-session.machine.test.ts +0 -109
- package/src/utils/cognito-session.refresh.test.ts +0 -84
- package/src/utils/cognito-session.test.ts +0 -328
- package/src/utils/cognito-session.ts +0 -448
- package/src/utils/company-selection-error.test.ts +0 -42
- package/src/utils/company-selection-error.ts +0 -45
- package/src/utils/contribution-table.ts +0 -83
- package/src/utils/environmental-error.test.ts +0 -45
- package/src/utils/environmental-error.ts +0 -39
- package/src/utils/epipe.test.ts +0 -28
- package/src/utils/epipe.ts +0 -29
- package/src/utils/expected-cli-error.test.ts +0 -28
- package/src/utils/expected-cli-error.ts +0 -39
- package/src/utils/feedback-diagnostics.test.ts +0 -183
- package/src/utils/feedback-diagnostics.ts +0 -123
- package/src/utils/feedback-screenshots.test.ts +0 -134
- package/src/utils/feedback-screenshots.ts +0 -124
- package/src/utils/feedback-versions.test.ts +0 -98
- package/src/utils/feedback-versions.ts +0 -68
- package/src/utils/git.ts +0 -74
- package/src/utils/integrity.ts +0 -59
- package/src/utils/intercepted-process-exit.test.ts +0 -37
- package/src/utils/intercepted-process-exit.ts +0 -36
- package/src/utils/login-provider.test.ts +0 -42
- package/src/utils/login-provider.ts +0 -30
- package/src/utils/manifest.test.ts +0 -167
- package/src/utils/manifest.ts +0 -131
- package/src/utils/pack-contributions.test.ts +0 -603
- package/src/utils/pack-contributions.ts +0 -450
- package/src/utils/people.ts +0 -215
- package/src/utils/registry-client.ts +0 -204
- package/src/utils/registry.ts +0 -90
- package/src/utils/resolve-vault-credential.test.ts +0 -69
- package/src/utils/resolve-vault-credential.ts +0 -60
- package/src/utils/sandbox-runner-client.test.ts +0 -279
- package/src/utils/sandbox-runner-client.ts +0 -260
- package/src/utils/secrets-cache.ts +0 -183
- package/src/utils/sentry-identity.ts +0 -45
- package/src/utils/settle-with-timeout.test.ts +0 -21
- package/src/utils/settle-with-timeout.ts +0 -22
- package/src/utils/user-yaml-error.test.ts +0 -24
- package/src/utils/user-yaml-error.ts +0 -30
- package/src/utils/vault-api.test.ts +0 -536
- package/src/utils/vault-api.ts +0 -382
- package/src/utils/version-check.test.ts +0 -176
- package/src/utils/version-check.ts +0 -155
- package/src/utils/version-gate.test.ts +0 -1024
- package/src/utils/version-gate.ts +0 -702
- package/test/commands/db-tenant-isolation.test.ts +0 -94
- package/test/commands/db.test.ts +0 -85
- package/test/commands/signals.test.ts +0 -207
- package/test/commands/sources.test.ts +0 -231
- package/test/e2e/smoke-install-mcp.sh +0 -113
- package/test/fixtures/hq-pack-smoke-mcp/mcp/smoke-http.json +0 -1
- package/test/fixtures/hq-pack-smoke-mcp/package.yaml +0 -11
- package/test/fixtures/signals/action_item/sample.md +0 -16
- package/test/fixtures/signals/summary/sample.md +0 -12
- package/test/fixtures/sources/meetings/sample.md +0 -25
- package/test/helpers/cli-runner.ts +0 -150
- package/test/helpers/s3-list-mock.ts +0 -79
- package/test/helpers/vault-service-mock.ts +0 -295
- package/test/sources-signals/smoke.test.ts +0 -228
- package/tsconfig.json +0 -20
- package/vitest.config.ts +0 -11
- package/vitest.e2e.config.ts +0 -7
package/src/commands/packs.ts
DELETED
|
@@ -1,622 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* `hq packs` -- lifecycle for CONTENT packs (the ones installed via
|
|
3
|
-
* `hq install <source>` that live in `core/packages/hq-pack-<name>/`).
|
|
4
|
-
*
|
|
5
|
-
* hq packs list Installed packs + curated catalog, with link health.
|
|
6
|
-
* hq packs update Re-install the latest of an installed pack.
|
|
7
|
-
* hq packs uninstall Un-wire + archive a pack (clean, no dangling symlinks).
|
|
8
|
-
*
|
|
9
|
-
* Also exposed through `hq packages list`, because package installs use this
|
|
10
|
-
* content-pack system. Content packs have no registry file -- the filesystem
|
|
11
|
-
* under core/packages/ is the source of truth.
|
|
12
|
-
*
|
|
13
|
-
* Every subcommand supports `--json` for machine consumers (the HQ Sync
|
|
14
|
-
* menubar app). JSON is also the default when stdout is not a TTY, matching
|
|
15
|
-
* the `hq signals` / `hq sources` convention.
|
|
16
|
-
*
|
|
17
|
-
* Spec: knowledge/public/hq-core/package-yaml-spec.md.
|
|
18
|
-
*/
|
|
19
|
-
|
|
20
|
-
import * as fs from 'fs';
|
|
21
|
-
import * as path from 'path';
|
|
22
|
-
import * as readline from 'readline';
|
|
23
|
-
import { spawnSync } from 'child_process';
|
|
24
|
-
import { Command } from 'commander';
|
|
25
|
-
import chalk from 'chalk';
|
|
26
|
-
import semverSatisfies from 'semver/functions/satisfies.js';
|
|
27
|
-
import { findHqRoot } from '../utils/manifest.js';
|
|
28
|
-
import {
|
|
29
|
-
classify,
|
|
30
|
-
resolveLatest,
|
|
31
|
-
resolveLatestMarketplace,
|
|
32
|
-
runScanPackages,
|
|
33
|
-
installPack,
|
|
34
|
-
type LatestResult,
|
|
35
|
-
} from './pack-install.js';
|
|
36
|
-
import {
|
|
37
|
-
contributionLinks,
|
|
38
|
-
linkStatus,
|
|
39
|
-
listInstalledPacks,
|
|
40
|
-
findDependentPacks,
|
|
41
|
-
readPackManifest,
|
|
42
|
-
unwirePack,
|
|
43
|
-
unwirePackMcp,
|
|
44
|
-
readHqVersion,
|
|
45
|
-
readRecommendedPackages,
|
|
46
|
-
packagesDir,
|
|
47
|
-
type InstalledPack,
|
|
48
|
-
type LinkStatus,
|
|
49
|
-
} from '../utils/pack-contributions.js';
|
|
50
|
-
import type { PackContributeKey } from '../types.js';
|
|
51
|
-
|
|
52
|
-
// ---------------------------------------------------------------------------
|
|
53
|
-
// Shared helpers
|
|
54
|
-
// ---------------------------------------------------------------------------
|
|
55
|
-
|
|
56
|
-
interface CommonOpts {
|
|
57
|
-
json?: boolean;
|
|
58
|
-
hqRoot?: string;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function resolveRoot(opts: CommonOpts): string {
|
|
62
|
-
return opts.hqRoot ? path.resolve(opts.hqRoot) : findHqRoot();
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
/** JSON when --json or when piped (non-TTY); human-readable on a terminal. */
|
|
66
|
-
function wantsJson(opts: CommonOpts): boolean {
|
|
67
|
-
return opts.json === true || !process.stdout.isTTY;
|
|
68
|
-
}
|
|
69
|
-
|
|
70
|
-
function emitJson(value: unknown): void {
|
|
71
|
-
process.stdout.write(JSON.stringify(value, null, 2) + '\n');
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
function safeClassify(source?: string): string | null {
|
|
75
|
-
if (!source) return null;
|
|
76
|
-
try {
|
|
77
|
-
return classify(source);
|
|
78
|
-
} catch {
|
|
79
|
-
return null;
|
|
80
|
-
}
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
function evalConditional(expr: string): boolean {
|
|
84
|
-
return spawnSync('bash', ['-c', expr], { stdio: 'ignore' }).status === 0;
|
|
85
|
-
}
|
|
86
|
-
|
|
87
|
-
async function confirm(question: string): Promise<boolean> {
|
|
88
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
89
|
-
const answer: string = await new Promise((resolve) => {
|
|
90
|
-
rl.question(`${question} [y/N] `, (a) => {
|
|
91
|
-
rl.close();
|
|
92
|
-
resolve(a);
|
|
93
|
-
});
|
|
94
|
-
});
|
|
95
|
-
return /^(y|yes)$/i.test(answer.trim());
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
// ---------------------------------------------------------------------------
|
|
99
|
-
// list
|
|
100
|
-
// ---------------------------------------------------------------------------
|
|
101
|
-
|
|
102
|
-
interface InstalledPackView {
|
|
103
|
-
name: string;
|
|
104
|
-
version?: string;
|
|
105
|
-
publisher?: string;
|
|
106
|
-
source?: string;
|
|
107
|
-
transport: string | null;
|
|
108
|
-
requiresHqCore?: string;
|
|
109
|
-
hqCoreSatisfied: boolean | null;
|
|
110
|
-
contributes: Partial<Record<PackContributeKey, number>>;
|
|
111
|
-
links: Record<LinkStatus, number>;
|
|
112
|
-
brokenLinks: Array<{ key: PackContributeKey; item: string; dst: string }>;
|
|
113
|
-
inCatalog: boolean;
|
|
114
|
-
updateAvailable: boolean | null;
|
|
115
|
-
/**
|
|
116
|
-
* Post-install initialization (US-005). Present only when the pack's
|
|
117
|
-
* package.yaml declares an `initialization` block — drives the HQ Sync
|
|
118
|
-
* "Installed" panel get-started affordance. Absent → omitted (no null noise).
|
|
119
|
-
*/
|
|
120
|
-
initialization?: { entrypoint: string; prompt?: string };
|
|
121
|
-
error?: string;
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
interface AvailablePackView {
|
|
125
|
-
source: string;
|
|
126
|
-
description?: string;
|
|
127
|
-
installed: false;
|
|
128
|
-
conditional?: string;
|
|
129
|
-
conditionalStatus: 'pass' | 'fail' | 'unevaluated' | 'none';
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
interface PacksListView {
|
|
133
|
-
hqRoot: string;
|
|
134
|
-
hqVersion: string | null;
|
|
135
|
-
installed: InstalledPackView[];
|
|
136
|
-
available: AvailablePackView[];
|
|
137
|
-
warnings: string[];
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export function buildInstalledView(
|
|
141
|
-
hqRoot: string,
|
|
142
|
-
hqVersion: string | null,
|
|
143
|
-
pack: InstalledPack,
|
|
144
|
-
installedSources: Set<string>,
|
|
145
|
-
checkUpdates: boolean,
|
|
146
|
-
latestProbe?: LatestResult,
|
|
147
|
-
): InstalledPackView {
|
|
148
|
-
if (!pack.manifest) {
|
|
149
|
-
return {
|
|
150
|
-
name: pack.name,
|
|
151
|
-
transport: null,
|
|
152
|
-
hqCoreSatisfied: null,
|
|
153
|
-
contributes: {},
|
|
154
|
-
links: { live: 0, broken: 0, missing: 0, foreign: 0 },
|
|
155
|
-
brokenLinks: [],
|
|
156
|
-
inCatalog: false,
|
|
157
|
-
updateAvailable: null,
|
|
158
|
-
error: pack.error ?? 'unreadable manifest',
|
|
159
|
-
};
|
|
160
|
-
}
|
|
161
|
-
const m = pack.manifest;
|
|
162
|
-
const contributes = m.contributes ?? {};
|
|
163
|
-
const links = contributionLinks(hqRoot, pack.dir, contributes);
|
|
164
|
-
const counts: Record<LinkStatus, number> = { live: 0, broken: 0, missing: 0, foreign: 0 };
|
|
165
|
-
const brokenLinks: InstalledPackView['brokenLinks'] = [];
|
|
166
|
-
for (const link of links) {
|
|
167
|
-
const st = linkStatus(link);
|
|
168
|
-
counts[st]++;
|
|
169
|
-
if (st === 'broken') brokenLinks.push({ key: link.key, item: link.item, dst: link.dst });
|
|
170
|
-
}
|
|
171
|
-
const contributeCounts: Partial<Record<PackContributeKey, number>> = {};
|
|
172
|
-
for (const [key, items] of Object.entries(contributes) as [PackContributeKey, unknown][]) {
|
|
173
|
-
if (Array.isArray(items) && items.length > 0) contributeCounts[key] = items.length;
|
|
174
|
-
}
|
|
175
|
-
const requiresHqCore = m.requires?.hqCore;
|
|
176
|
-
const hqCoreSatisfied =
|
|
177
|
-
hqVersion && requiresHqCore ? semverSatisfies(hqVersion, requiresHqCore) : null;
|
|
178
|
-
|
|
179
|
-
let updateAvailable: boolean | null = null;
|
|
180
|
-
if (checkUpdates && m.source) {
|
|
181
|
-
updateAvailable = latestProbe?.updateAvailable ?? null;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
// US-005 — surface the pack's `initialization` block so the HQ Sync
|
|
185
|
-
// "Installed" panel can render its get-started affordance. `readPackManifest`
|
|
186
|
-
// already parses the full package.yaml, so `m.initialization` is available;
|
|
187
|
-
// we still shape it defensively (tolerate a malformed/absent block) and omit
|
|
188
|
-
// the field entirely when absent so the JSON carries no null noise.
|
|
189
|
-
let initialization: InstalledPackView['initialization'];
|
|
190
|
-
const rawInit = m.initialization as unknown;
|
|
191
|
-
if (rawInit && typeof rawInit === 'object' && !Array.isArray(rawInit)) {
|
|
192
|
-
const initObj = rawInit as Record<string, unknown>;
|
|
193
|
-
const entrypoint = initObj.entrypoint;
|
|
194
|
-
if (typeof entrypoint === 'string' && entrypoint.trim() !== '') {
|
|
195
|
-
initialization = { entrypoint };
|
|
196
|
-
if (typeof initObj.prompt === 'string') initialization.prompt = initObj.prompt;
|
|
197
|
-
}
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
return {
|
|
201
|
-
name: m.name ?? pack.name,
|
|
202
|
-
version: m.version,
|
|
203
|
-
publisher: m.publisher,
|
|
204
|
-
source: m.source,
|
|
205
|
-
transport: safeClassify(m.source),
|
|
206
|
-
requiresHqCore,
|
|
207
|
-
hqCoreSatisfied,
|
|
208
|
-
contributes: contributeCounts,
|
|
209
|
-
links: counts,
|
|
210
|
-
brokenLinks,
|
|
211
|
-
inCatalog: m.source ? installedSources.has(m.source) : false,
|
|
212
|
-
updateAvailable,
|
|
213
|
-
...(initialization ? { initialization } : {}),
|
|
214
|
-
};
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
async function buildListView(
|
|
218
|
-
hqRoot: string,
|
|
219
|
-
checkUpdates: boolean,
|
|
220
|
-
evalConditionals: boolean,
|
|
221
|
-
refreshUpdates: boolean,
|
|
222
|
-
): Promise<PacksListView> {
|
|
223
|
-
const hqVersion = readHqVersion(hqRoot);
|
|
224
|
-
const packs = listInstalledPacks(hqRoot);
|
|
225
|
-
const catalog = readRecommendedPackages(hqRoot);
|
|
226
|
-
const catalogSources = new Set(catalog.map((c) => c.source));
|
|
227
|
-
const warnings: string[] = [];
|
|
228
|
-
|
|
229
|
-
const latestProbes = await Promise.all(
|
|
230
|
-
packs.map((p) =>
|
|
231
|
-
checkUpdates && p.manifest?.source
|
|
232
|
-
? resolveLatest(p.manifest.source, p.manifest.version, { forceRefresh: refreshUpdates })
|
|
233
|
-
: Promise.resolve(undefined),
|
|
234
|
-
),
|
|
235
|
-
);
|
|
236
|
-
const installed = packs.map((p, i) =>
|
|
237
|
-
buildInstalledView(hqRoot, hqVersion, p, catalogSources, checkUpdates, latestProbes[i]),
|
|
238
|
-
);
|
|
239
|
-
for (const p of installed) {
|
|
240
|
-
if (p.error) warnings.push(`${p.name}: ${p.error}`);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
const installedSources = new Set(
|
|
244
|
-
packs.map((p) => p.manifest?.source).filter((s): s is string => !!s),
|
|
245
|
-
);
|
|
246
|
-
const available: AvailablePackView[] = catalog
|
|
247
|
-
.filter((c) => !installedSources.has(c.source))
|
|
248
|
-
.map((c) => {
|
|
249
|
-
let conditionalStatus: AvailablePackView['conditionalStatus'] = c.conditional
|
|
250
|
-
? 'unevaluated'
|
|
251
|
-
: 'none';
|
|
252
|
-
if (c.conditional && evalConditionals) {
|
|
253
|
-
conditionalStatus = evalConditional(c.conditional) ? 'pass' : 'fail';
|
|
254
|
-
}
|
|
255
|
-
return {
|
|
256
|
-
source: c.source,
|
|
257
|
-
description: c.description,
|
|
258
|
-
installed: false,
|
|
259
|
-
conditional: c.conditional,
|
|
260
|
-
conditionalStatus,
|
|
261
|
-
};
|
|
262
|
-
});
|
|
263
|
-
|
|
264
|
-
return { hqRoot, hqVersion, installed, available, warnings };
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
function printListHuman(view: PacksListView): void {
|
|
268
|
-
console.log(chalk.bold(`\nHQ packs (${view.hqRoot}, hqCore ${view.hqVersion ?? '?'})\n`));
|
|
269
|
-
if (view.installed.length === 0) {
|
|
270
|
-
console.log(chalk.dim(' No content packs installed.\n'));
|
|
271
|
-
} else {
|
|
272
|
-
console.log(chalk.bold('Installed:'));
|
|
273
|
-
for (const p of view.installed) {
|
|
274
|
-
if (p.error) {
|
|
275
|
-
console.log(` ${chalk.red(p.name)} ${chalk.dim('(' + p.error + ')')}`);
|
|
276
|
-
continue;
|
|
277
|
-
}
|
|
278
|
-
const broken = p.links.broken > 0 ? chalk.red(` ! ${p.links.broken} broken link(s)`) : '';
|
|
279
|
-
const upd = p.updateAvailable ? chalk.yellow(' ^ update available') : '';
|
|
280
|
-
const hq = p.hqCoreSatisfied === false ? chalk.red(' x hqCore mismatch') : '';
|
|
281
|
-
console.log(` ${chalk.green(p.name)}@${p.version ?? '?'} ${chalk.dim(p.transport ?? '')}${upd}${hq}${broken}`);
|
|
282
|
-
}
|
|
283
|
-
console.log();
|
|
284
|
-
}
|
|
285
|
-
if (view.available.length > 0) {
|
|
286
|
-
console.log(chalk.bold('Available (curated):'));
|
|
287
|
-
for (const a of view.available) {
|
|
288
|
-
const gate =
|
|
289
|
-
a.conditionalStatus === 'fail'
|
|
290
|
-
? chalk.dim(' (gated off)')
|
|
291
|
-
: a.conditionalStatus === 'unevaluated' && a.conditional
|
|
292
|
-
? chalk.dim(' (conditional)')
|
|
293
|
-
: '';
|
|
294
|
-
console.log(` ${chalk.cyan(a.source)}${gate}`);
|
|
295
|
-
if (a.description) console.log(chalk.dim(` ${a.description}`));
|
|
296
|
-
}
|
|
297
|
-
console.log(chalk.dim('\n Install with: hq install <source>\n'));
|
|
298
|
-
}
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// ---------------------------------------------------------------------------
|
|
302
|
-
// update
|
|
303
|
-
// ---------------------------------------------------------------------------
|
|
304
|
-
|
|
305
|
-
interface UpdateCheck {
|
|
306
|
-
name: string;
|
|
307
|
-
transport: string;
|
|
308
|
-
current?: string;
|
|
309
|
-
latest?: string;
|
|
310
|
-
updateAvailable: boolean | null;
|
|
311
|
-
applied: boolean;
|
|
312
|
-
reason?: string;
|
|
313
|
-
error?: string;
|
|
314
|
-
}
|
|
315
|
-
|
|
316
|
-
interface UpdateOpts extends CommonOpts {
|
|
317
|
-
checkOnly?: boolean;
|
|
318
|
-
refresh?: boolean;
|
|
319
|
-
yes?: boolean;
|
|
320
|
-
allowHooks?: boolean;
|
|
321
|
-
allowMcp?: boolean;
|
|
322
|
-
branch?: boolean;
|
|
323
|
-
}
|
|
324
|
-
|
|
325
|
-
async function runUpdate(name: string | undefined, opts: UpdateOpts): Promise<UpdateCheck[]> {
|
|
326
|
-
const hqRoot = resolveRoot(opts);
|
|
327
|
-
let packs = listInstalledPacks(hqRoot).filter((p) => p.manifest);
|
|
328
|
-
if (name) {
|
|
329
|
-
packs = packs.filter((p) => (p.manifest?.name ?? p.name) === name);
|
|
330
|
-
if (packs.length === 0) {
|
|
331
|
-
return [{ name, transport: 'unknown', updateAvailable: null, applied: false, error: 'not installed' }];
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
|
|
335
|
-
const results: UpdateCheck[] = [];
|
|
336
|
-
for (const pack of packs) {
|
|
337
|
-
const m = pack.manifest!;
|
|
338
|
-
const source = m.source;
|
|
339
|
-
const pname = m.name ?? pack.name;
|
|
340
|
-
if (!source) {
|
|
341
|
-
results.push({ name: pname, transport: 'unknown', updateAvailable: null, applied: false, error: 'no stamped source -- cannot update' });
|
|
342
|
-
continue;
|
|
343
|
-
}
|
|
344
|
-
// US-006: marketplace sources need the async listings probe (network) —
|
|
345
|
-
// the sync `resolveLatest` cannot make that call and returns null. Every
|
|
346
|
-
// other transport keeps the existing synchronous probe unchanged.
|
|
347
|
-
const probe: LatestResult =
|
|
348
|
-
safeClassify(source) === 'marketplace'
|
|
349
|
-
? await resolveLatestMarketplace(source, m.version)
|
|
350
|
-
: await resolveLatest(source, m.version, { forceRefresh: true });
|
|
351
|
-
const base: UpdateCheck = {
|
|
352
|
-
name: pname,
|
|
353
|
-
transport: probe.transport,
|
|
354
|
-
current: probe.current,
|
|
355
|
-
latest: probe.latest,
|
|
356
|
-
updateAvailable: probe.updateAvailable,
|
|
357
|
-
applied: false,
|
|
358
|
-
error: probe.error,
|
|
359
|
-
};
|
|
360
|
-
if (opts.checkOnly) {
|
|
361
|
-
results.push(base);
|
|
362
|
-
continue;
|
|
363
|
-
}
|
|
364
|
-
// Apply only when there's a known update, or when explicitly named (force re-sync).
|
|
365
|
-
if (probe.updateAvailable === false && !name) {
|
|
366
|
-
results.push({ ...base, reason: 'already current' });
|
|
367
|
-
continue;
|
|
368
|
-
}
|
|
369
|
-
try {
|
|
370
|
-
// Un-wire old contributions first so a contribution dropped by the new
|
|
371
|
-
// version doesn't leave a dangling host symlink. installPack re-wires.
|
|
372
|
-
unwirePack(hqRoot, pack.dir, m.contributes ?? {});
|
|
373
|
-
const prevCwd = process.cwd();
|
|
374
|
-
if (opts.hqRoot) process.chdir(hqRoot); // installPack resolves via findHqRoot()
|
|
375
|
-
try {
|
|
376
|
-
await installPack(source, {
|
|
377
|
-
allowHooks: opts.yes || opts.allowHooks,
|
|
378
|
-
allowMcp: opts.yes || opts.allowMcp,
|
|
379
|
-
followBranch: opts.branch,
|
|
380
|
-
quiet: wantsJson(opts),
|
|
381
|
-
});
|
|
382
|
-
} finally {
|
|
383
|
-
if (opts.hqRoot) process.chdir(prevCwd);
|
|
384
|
-
}
|
|
385
|
-
results.push({ ...base, applied: true });
|
|
386
|
-
} catch (e) {
|
|
387
|
-
results.push({ ...base, applied: false, error: (e as Error).message });
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
return results;
|
|
391
|
-
}
|
|
392
|
-
|
|
393
|
-
// ---------------------------------------------------------------------------
|
|
394
|
-
// uninstall
|
|
395
|
-
// ---------------------------------------------------------------------------
|
|
396
|
-
|
|
397
|
-
interface UninstallResult {
|
|
398
|
-
name: string;
|
|
399
|
-
archived: string | null;
|
|
400
|
-
unlinked: Array<{ key: PackContributeKey; item: string; dst: string }>;
|
|
401
|
-
skipped: Array<{ key: PackContributeKey; item: string; dst: string; reason: string }>;
|
|
402
|
-
rescan: 'ok' | 'skipped';
|
|
403
|
-
workersRegistryRegenerated: boolean;
|
|
404
|
-
sideEffectsSuggested: string[];
|
|
405
|
-
warnings: string[];
|
|
406
|
-
}
|
|
407
|
-
|
|
408
|
-
interface UninstallOpts extends CommonOpts {
|
|
409
|
-
yes?: boolean;
|
|
410
|
-
archive?: boolean; // commander sets false for --no-archive
|
|
411
|
-
force?: boolean; // bypass the dependents guard (M0)
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
function archiveTimestamp(): string {
|
|
415
|
-
return new Date().toISOString().replace(/[:.]/g, '-').replace('T', '_').slice(0, 19);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
async function runUninstall(name: string, opts: UninstallOpts): Promise<UninstallResult> {
|
|
419
|
-
const hqRoot = resolveRoot(opts);
|
|
420
|
-
const packDir = path.join(packagesDir(hqRoot), name);
|
|
421
|
-
if (!fs.existsSync(packDir)) {
|
|
422
|
-
throw new Error(`Pack "${name}" is not installed (no core/packages/${name}/).`);
|
|
423
|
-
}
|
|
424
|
-
const warnings: string[] = [];
|
|
425
|
-
const { manifest } = readPackManifest(packDir);
|
|
426
|
-
const contributes = manifest?.contributes ?? {};
|
|
427
|
-
if (!manifest) {
|
|
428
|
-
warnings.push('package.yaml unreadable -- host symlinks could not be computed precisely; ran a re-scan to reconcile.');
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
// 0. Dependents guard (M0): refuse to remove a pack that another installed pack
|
|
432
|
-
// lists in its `requires.packs`, unless --force. Filesystem presence is the
|
|
433
|
-
// source of truth (listInstalledPacks), consistent with the install-time
|
|
434
|
-
// assertPackDependencies check. Runs BEFORE any un-wiring so a blocked uninstall
|
|
435
|
-
// leaves the pack fully intact.
|
|
436
|
-
if (!opts.force) {
|
|
437
|
-
const dependents = findDependentPacks(listInstalledPacks(hqRoot), name);
|
|
438
|
-
if (dependents.length > 0) {
|
|
439
|
-
const who = dependents.map((p) => p.manifest?.name ?? p.name).join(', ');
|
|
440
|
-
throw new Error(
|
|
441
|
-
`Cannot uninstall "${name}": required by ${who}. ` +
|
|
442
|
-
`Uninstall the dependent pack(s) first, or pass --force to override.`,
|
|
443
|
-
);
|
|
444
|
-
}
|
|
445
|
-
}
|
|
446
|
-
|
|
447
|
-
// 1. Un-wire only our symlinks.
|
|
448
|
-
const { unlinked, skipped } = unwirePack(hqRoot, packDir, contributes);
|
|
449
|
-
|
|
450
|
-
// 1b. Un-register the pack's MCP (`wire: 'merge'`) servers — invisible to the
|
|
451
|
-
// symlink unwire above. Provenance-scoped: removes ONLY entries stamped with this
|
|
452
|
-
// pack's `_hqPack`, and skip-and-warns on any foreign/unstamped same-named entry.
|
|
453
|
-
// Tolerant of a Codex-less host / absent config; idempotent on re-run.
|
|
454
|
-
try {
|
|
455
|
-
const mcp = unwirePackMcp(name, contributes);
|
|
456
|
-
for (const server of mcp.servers) {
|
|
457
|
-
if ('skipped' in server.claude) continue; // (claude is always inspected; never skipped)
|
|
458
|
-
if (server.claude.outcome === 'skipped-foreign' && server.claude.reason) {
|
|
459
|
-
warnings.push(server.claude.reason);
|
|
460
|
-
}
|
|
461
|
-
if (!('skipped' in server.codex) && server.codex.outcome === 'skipped-foreign' && server.codex.reason) {
|
|
462
|
-
warnings.push(server.codex.reason);
|
|
463
|
-
}
|
|
464
|
-
}
|
|
465
|
-
} catch (e) {
|
|
466
|
-
// Never let an MCP un-registration failure abort the rest of the uninstall
|
|
467
|
-
// (symlink unwire already ran; the pack dir still gets archived). Surface it.
|
|
468
|
-
warnings.push(`MCP un-registration encountered an error: ${(e as Error).message}`);
|
|
469
|
-
}
|
|
470
|
-
|
|
471
|
-
// 2. Archive (or delete) the pack dir -- BEFORE re-scan so it isn't re-wired.
|
|
472
|
-
let archived: string | null = null;
|
|
473
|
-
if (opts.archive === false) {
|
|
474
|
-
fs.rmSync(packDir, { recursive: true, force: true });
|
|
475
|
-
} else {
|
|
476
|
-
const dest = path.join(packagesDir(hqRoot), '.archive', `${name}-${archiveTimestamp()}`);
|
|
477
|
-
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
478
|
-
fs.renameSync(packDir, dest);
|
|
479
|
-
archived = path.relative(hqRoot, dest);
|
|
480
|
-
}
|
|
481
|
-
|
|
482
|
-
// 3. Re-assert remaining packs' links (handles two packs sharing a contribution).
|
|
483
|
-
let rescan: 'ok' | 'skipped' = 'skipped';
|
|
484
|
-
try {
|
|
485
|
-
// Quiet: uninstall emits its own summary / JSON; keep scan stdout off ours.
|
|
486
|
-
runScanPackages(hqRoot, { quiet: true });
|
|
487
|
-
rescan = 'ok';
|
|
488
|
-
} catch {
|
|
489
|
-
rescan = 'skipped';
|
|
490
|
-
}
|
|
491
|
-
|
|
492
|
-
// 4. Regenerate the workers registry if this pack contributed workers.
|
|
493
|
-
let workersRegistryRegenerated = false;
|
|
494
|
-
const contributedWorkers = Array.isArray(contributes.workers) && contributes.workers.length > 0;
|
|
495
|
-
if (contributedWorkers) {
|
|
496
|
-
const gen = path.join(hqRoot, 'core', 'scripts', 'generate-workers-registry.sh');
|
|
497
|
-
if (fs.existsSync(gen)) {
|
|
498
|
-
const r = spawnSync('bash', [gen], { cwd: hqRoot, env: { ...process.env, HQ_ROOT: hqRoot }, stdio: 'ignore' });
|
|
499
|
-
workersRegistryRegenerated = r.status === 0;
|
|
500
|
-
if (!workersRegistryRegenerated) warnings.push('generate-workers-registry.sh exited non-zero');
|
|
501
|
-
}
|
|
502
|
-
}
|
|
503
|
-
|
|
504
|
-
const sideEffectsSuggested = ['master-sync', 'qmd-reindex'];
|
|
505
|
-
if (contributedWorkers && !workersRegistryRegenerated) sideEffectsSuggested.push('workers-registry');
|
|
506
|
-
|
|
507
|
-
return {
|
|
508
|
-
name,
|
|
509
|
-
archived,
|
|
510
|
-
unlinked,
|
|
511
|
-
skipped,
|
|
512
|
-
rescan,
|
|
513
|
-
workersRegistryRegenerated,
|
|
514
|
-
sideEffectsSuggested,
|
|
515
|
-
warnings,
|
|
516
|
-
};
|
|
517
|
-
}
|
|
518
|
-
|
|
519
|
-
// ---------------------------------------------------------------------------
|
|
520
|
-
// Registration
|
|
521
|
-
// ---------------------------------------------------------------------------
|
|
522
|
-
|
|
523
|
-
export function registerPacksListCommand(parent: Command): void {
|
|
524
|
-
parent
|
|
525
|
-
.command('list')
|
|
526
|
-
.alias('ls')
|
|
527
|
-
.description('List installed content packs and the curated catalog')
|
|
528
|
-
.option('--json', 'Machine-readable JSON output')
|
|
529
|
-
.option('--hq-root <path>', 'HQ root (default: auto-detect)')
|
|
530
|
-
.option('--check-updates', 'Probe each pack for available updates (network I/O)')
|
|
531
|
-
.option('--refresh', 'Bypass cached update probes')
|
|
532
|
-
.option('--eval-conditionals', 'Evaluate catalog conditional predicates (runs bash)')
|
|
533
|
-
.action(
|
|
534
|
-
async (opts: CommonOpts & { checkUpdates?: boolean; evalConditionals?: boolean; refresh?: boolean }) => {
|
|
535
|
-
try {
|
|
536
|
-
const view = await buildListView(
|
|
537
|
-
resolveRoot(opts),
|
|
538
|
-
!!opts.checkUpdates,
|
|
539
|
-
!!opts.evalConditionals,
|
|
540
|
-
!!opts.refresh,
|
|
541
|
-
);
|
|
542
|
-
if (wantsJson(opts)) emitJson(view);
|
|
543
|
-
else printListHuman(view);
|
|
544
|
-
} catch (e) {
|
|
545
|
-
console.error(chalk.red('packs list failed:'), (e as Error).message);
|
|
546
|
-
process.exit(1);
|
|
547
|
-
}
|
|
548
|
-
},
|
|
549
|
-
);
|
|
550
|
-
}
|
|
551
|
-
|
|
552
|
-
export function registerPacksCommand(parent: Command): void {
|
|
553
|
-
const packs = parent.command('packs').description('Content-pack lifecycle (install via `hq install`)');
|
|
554
|
-
|
|
555
|
-
registerPacksListCommand(packs);
|
|
556
|
-
|
|
557
|
-
packs
|
|
558
|
-
.command('update [name]')
|
|
559
|
-
.description('Update an installed content pack (re-install latest)')
|
|
560
|
-
.option('--json', 'Machine-readable JSON output')
|
|
561
|
-
.option('--hq-root <path>', 'HQ root (default: auto-detect)')
|
|
562
|
-
.option('--check-only', 'Report availability without installing')
|
|
563
|
-
.option('--refresh', 'Bypass cached update probes (update refreshes by default)')
|
|
564
|
-
.option('-y, --yes', 'Non-interactive (implies --allow-hooks and --allow-mcp)')
|
|
565
|
-
.option('--allow-hooks', 'Install pack hooks without prompting')
|
|
566
|
-
.option('--allow-mcp', 'Register pack MCP servers without prompting')
|
|
567
|
-
.option('--branch', 'Follow the source branch instead of SHA-pinning')
|
|
568
|
-
.action(async (name: string | undefined, opts: UpdateOpts) => {
|
|
569
|
-
try {
|
|
570
|
-
const results = await runUpdate(name, opts);
|
|
571
|
-
if (wantsJson(opts)) {
|
|
572
|
-
emitJson({ checked: results, updatedCount: results.filter((r) => r.applied).length });
|
|
573
|
-
} else {
|
|
574
|
-
for (const r of results) {
|
|
575
|
-
if (r.error) console.log(` ${chalk.red(r.name)}: ${r.error}`);
|
|
576
|
-
else if (r.applied) console.log(` ${chalk.green(r.name)}: updated`);
|
|
577
|
-
else if (r.updateAvailable) console.log(` ${chalk.yellow(r.name)}: ${r.current ?? '?'} -> ${r.latest ?? '?'} available`);
|
|
578
|
-
else console.log(` ${chalk.dim(r.name)}: ${r.reason ?? 'current'}`);
|
|
579
|
-
}
|
|
580
|
-
}
|
|
581
|
-
} catch (e) {
|
|
582
|
-
console.error(chalk.red('packs update failed:'), (e as Error).message);
|
|
583
|
-
process.exit(1);
|
|
584
|
-
}
|
|
585
|
-
});
|
|
586
|
-
|
|
587
|
-
packs
|
|
588
|
-
.command('uninstall <name>')
|
|
589
|
-
.alias('remove')
|
|
590
|
-
.description('Un-wire and archive an installed content pack')
|
|
591
|
-
.option('--json', 'Machine-readable JSON output')
|
|
592
|
-
.option('--hq-root <path>', 'HQ root (default: auto-detect)')
|
|
593
|
-
.option('-y, --yes', 'Skip confirmation')
|
|
594
|
-
.option('--no-archive', 'Delete instead of archiving')
|
|
595
|
-
.option('--force', 'Uninstall even if other installed packs require this one')
|
|
596
|
-
.action(async (name: string, opts: UninstallOpts) => {
|
|
597
|
-
try {
|
|
598
|
-
if (!opts.yes) {
|
|
599
|
-
if (!process.stdout.isTTY) {
|
|
600
|
-
throw new Error('Refusing to uninstall without --yes in non-interactive mode.');
|
|
601
|
-
}
|
|
602
|
-
const ok = await confirm(`Uninstall pack "${name}"? This removes its host symlinks.`);
|
|
603
|
-
if (!ok) {
|
|
604
|
-
console.log(chalk.dim('Aborted.'));
|
|
605
|
-
return;
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
const result = await runUninstall(name, opts);
|
|
609
|
-
if (wantsJson(opts)) {
|
|
610
|
-
emitJson(result);
|
|
611
|
-
} else {
|
|
612
|
-
console.log(chalk.green(`\nOK Uninstalled ${name}`));
|
|
613
|
-
console.log(chalk.dim(` Unlinked ${result.unlinked.length} symlink(s); archived to ${result.archived ?? '(deleted)'}.`));
|
|
614
|
-
if (result.skipped.length > 0) console.log(chalk.dim(` Left ${result.skipped.length} non-owned path(s) in place.`));
|
|
615
|
-
for (const w of result.warnings) console.log(chalk.yellow(` ! ${w}`));
|
|
616
|
-
}
|
|
617
|
-
} catch (e) {
|
|
618
|
-
console.error(chalk.red('packs uninstall failed:'), (e as Error).message);
|
|
619
|
-
process.exit(1);
|
|
620
|
-
}
|
|
621
|
-
});
|
|
622
|
-
}
|