@phnx-labs/agents-cli 1.22.60 → 1.22.62
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 +53 -0
- package/README.md +6 -0
- package/dist/cli/command-registry.d.ts +1 -0
- package/dist/cli/command-registry.js +2 -0
- package/dist/commands/browser.js +9 -4
- package/dist/commands/doctor.js +1 -1
- package/dist/commands/exec.js +35 -1
- package/dist/commands/feed.js +3 -1
- package/dist/commands/harness-hooks.d.ts +55 -0
- package/dist/commands/harness-hooks.js +104 -0
- package/dist/commands/harness-wizard.d.ts +33 -14
- package/dist/commands/harness-wizard.js +53 -23
- package/dist/commands/harness.d.ts +14 -0
- package/dist/commands/harness.js +86 -5
- package/dist/commands/monitors.js +3 -3
- package/dist/commands/reminders.d.ts +9 -0
- package/dist/commands/reminders.js +49 -0
- package/dist/commands/run-account-picker.d.ts +14 -0
- package/dist/commands/run-account-picker.js +13 -0
- package/dist/commands/send.js +4 -1
- package/dist/commands/teams.d.ts +1 -1
- package/dist/commands/teams.js +9 -3
- package/dist/index.js +9 -0
- package/dist/lib/accounting/rotate.d.ts +63 -0
- package/dist/lib/accounting/rotate.js +229 -13
- package/dist/lib/browser/drivers/local.d.ts +11 -0
- package/dist/lib/browser/drivers/local.js +26 -0
- package/dist/lib/browser/profiles.js +8 -6
- package/dist/lib/browser/service.d.ts +12 -8
- package/dist/lib/browser/service.js +38 -10
- package/dist/lib/channels/owner-forward.d.ts +14 -8
- package/dist/lib/channels/owner-forward.js +9 -5
- package/dist/lib/channels/registry.d.ts +2 -0
- package/dist/lib/channels/send.js +13 -1
- package/dist/lib/claude-statusline.d.ts +14 -1
- package/dist/lib/claude-statusline.js +27 -2
- package/dist/lib/daemon/runner.js +17 -2
- package/dist/lib/devices/doctor-findings.d.ts +1 -1
- package/dist/lib/devices/doctor-findings.js +22 -4
- package/dist/lib/doctor-diff.d.ts +21 -5
- package/dist/lib/doctor-diff.js +242 -76
- package/dist/lib/feed/events.d.ts +1 -1
- package/dist/lib/feed/events.js +25 -16
- package/dist/lib/feed-broadcast.js +5 -12
- package/dist/lib/github/gh-overload.d.ts +58 -0
- package/dist/lib/github/gh-overload.js +246 -0
- package/dist/lib/github/rest.d.ts +64 -0
- package/dist/lib/github/rest.js +111 -0
- package/dist/lib/harness-connection-test.d.ts +57 -0
- package/dist/lib/harness-connection-test.js +80 -0
- package/dist/lib/heal.js +8 -3
- package/dist/lib/humans.d.ts +9 -0
- package/dist/lib/humans.js +29 -8
- package/dist/lib/installations/shims.d.ts +22 -0
- package/dist/lib/installations/shims.js +104 -0
- package/dist/lib/linear-project-counts.js +8 -0
- package/dist/lib/linear-rate-limit.d.ts +26 -0
- package/dist/lib/linear-rate-limit.js +163 -0
- package/dist/lib/mcp.d.ts +9 -0
- package/dist/lib/mcp.js +37 -1
- package/dist/lib/notify.d.ts +3 -0
- package/dist/lib/notify.js +63 -21
- package/dist/lib/open-url.js +5 -3
- package/dist/lib/permissions.d.ts +28 -0
- package/dist/lib/permissions.js +156 -1
- package/dist/lib/refresh.js +9 -1
- package/dist/lib/reminders.d.ts +29 -0
- package/dist/lib/reminders.js +88 -0
- package/dist/lib/resource-content-diff.d.ts +33 -0
- package/dist/lib/resource-content-diff.js +103 -0
- package/dist/lib/rules/compile.d.ts +7 -0
- package/dist/lib/rules/compile.js +7 -1
- package/dist/lib/session/active.d.ts +41 -4
- package/dist/lib/session/active.js +58 -7
- package/dist/lib/session/host-link.d.ts +22 -0
- package/dist/lib/session/host-link.js +40 -4
- package/dist/lib/session/trajectory.d.ts +42 -0
- package/dist/lib/session/trajectory.js +46 -27
- package/dist/lib/ssh-exec.d.ts +30 -0
- package/dist/lib/ssh-exec.js +37 -5
- package/dist/lib/startup/command-registry.js +1 -1
- package/dist/lib/subagents-registry.d.ts +18 -0
- package/dist/lib/subagents-registry.js +79 -0
- package/dist/lib/teams/agents.d.ts +12 -0
- package/dist/lib/teams/agents.js +51 -0
- package/dist/lib/traces/schema2-build.d.ts +85 -0
- package/dist/lib/traces/schema2-build.js +637 -0
- package/dist/lib/traces/schema2-danger.d.ts +36 -0
- package/dist/lib/traces/schema2-danger.js +185 -0
- package/dist/lib/traces/schema2.d.ts +149 -0
- package/dist/lib/traces/schema2.js +20 -0
- package/dist/lib/traces/sync.d.ts +93 -0
- package/dist/lib/traces/sync.js +75 -22
- package/dist/lib/traces/worker-template.js +5 -0
- package/dist/lib/uninstall.js +10 -1
- package/dist/lib/workflows.d.ts +11 -0
- package/dist/lib/workflows.js +67 -8
- package/package.json +1 -1
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { getOwnerNotifyFromHumans } from '../humans.js';
|
|
2
2
|
import { registerBuiltinProviders } from './providers/index.js';
|
|
3
3
|
import { resolveTransport } from './resolve.js';
|
|
4
|
+
import { sendToOwner } from '../notify.js';
|
|
4
5
|
const OWNER_ALIAS = 'owner';
|
|
5
6
|
/** True when the destination token means “the configured owner”. */
|
|
6
7
|
export function isOwnerAlias(to) {
|
|
@@ -112,6 +113,17 @@ export async function sendMessage(input, meta) {
|
|
|
112
113
|
const resolved = resolveSendEnvelope(input, meta);
|
|
113
114
|
if (!resolved.ok)
|
|
114
115
|
return { error: resolved.error };
|
|
115
|
-
const
|
|
116
|
+
const ownerPolicyRequest = resolved.envelope.ownerScoped === true
|
|
117
|
+
&& !input.channel?.trim()
|
|
118
|
+
&& (!input.to?.trim() || isOwnerAlias(input.to));
|
|
119
|
+
const result = ownerPolicyRequest
|
|
120
|
+
? await sendToOwner(resolved.envelope.text, {
|
|
121
|
+
meta,
|
|
122
|
+
dryRun: resolved.envelope.dryRun,
|
|
123
|
+
thread: resolved.envelope.thread,
|
|
124
|
+
attachments: resolved.envelope.attachments,
|
|
125
|
+
from: resolved.envelope.from,
|
|
126
|
+
})
|
|
127
|
+
: await deliverEnvelope(resolved.envelope, meta);
|
|
116
128
|
return { result, envelope: resolved.envelope };
|
|
117
129
|
}
|
|
@@ -9,6 +9,7 @@ export declare const CLAUDE_STATUSLINE_COMMAND = "agents __claude-statusline";
|
|
|
9
9
|
export declare function isStatusLineSelfReference(command: string): boolean;
|
|
10
10
|
interface ClaudeStatusLinePayload {
|
|
11
11
|
cwd?: string;
|
|
12
|
+
session_id?: string;
|
|
12
13
|
workspace?: {
|
|
13
14
|
current_dir?: string;
|
|
14
15
|
};
|
|
@@ -29,7 +30,19 @@ interface ClaudeStatusLinePayload {
|
|
|
29
30
|
}
|
|
30
31
|
export declare function ingestClaudeStatusLineUsage(payload: ClaudeStatusLinePayload, versionHome: string): boolean;
|
|
31
32
|
export declare function renderDelegate(payload: string, versionHome: string): string;
|
|
32
|
-
|
|
33
|
+
/**
|
|
34
|
+
* Format a reminder as a dimmed statusline part (empty string when none). The
|
|
35
|
+
* ◆ marker distinguishes it from the host/model/usage parts, and the ANSI dim
|
|
36
|
+
* keeps it quiet next to the live figures.
|
|
37
|
+
*/
|
|
38
|
+
export declare function formatReminderPart(short: string | undefined): string;
|
|
39
|
+
export declare function renderClaudeStatusLine(payload: ClaudeStatusLinePayload, host?: string, delegated?: string, reminder?: string): string;
|
|
40
|
+
/**
|
|
41
|
+
* Resolve the per-session reminder for the statusline, or '' when none is
|
|
42
|
+
* configured. A malformed reminders file is swallowed here on purpose — a broken
|
|
43
|
+
* prompt is worse than a missing reminder — while `agents reminders` surfaces it.
|
|
44
|
+
*/
|
|
45
|
+
export declare function resolveReminderPart(sessionId?: string): string;
|
|
33
46
|
export declare function runClaudeStatusLine(): Promise<number>;
|
|
34
47
|
export declare function installClaudeStatusLine(versionHome: string): {
|
|
35
48
|
changed: boolean;
|
|
@@ -5,6 +5,7 @@ import { spawnSync } from 'child_process';
|
|
|
5
5
|
import { readClaudeHomeConfig } from './agent-spec/agents.js';
|
|
6
6
|
import { atomicWriteFileSync } from './fs-atomic.js';
|
|
7
7
|
import { mergeClaudeUsageCacheWindows } from './accounting/usage.js';
|
|
8
|
+
import { loadReminders, pickReminderForSession } from './reminders.js';
|
|
8
9
|
export const CLAUDE_STATUSLINE_COMMAND = 'agents __claude-statusline';
|
|
9
10
|
const DELEGATE_FILE = path.join('.agents', 'claude-statusline-delegate');
|
|
10
11
|
// The private subcommand this feature runs. It is only ever invoked internally,
|
|
@@ -103,7 +104,16 @@ export function renderDelegate(payload, versionHome) {
|
|
|
103
104
|
});
|
|
104
105
|
return result.status === 0 ? result.stdout.trim() : '';
|
|
105
106
|
}
|
|
106
|
-
|
|
107
|
+
/**
|
|
108
|
+
* Format a reminder as a dimmed statusline part (empty string when none). The
|
|
109
|
+
* ◆ marker distinguishes it from the host/model/usage parts, and the ANSI dim
|
|
110
|
+
* keeps it quiet next to the live figures.
|
|
111
|
+
*/
|
|
112
|
+
export function formatReminderPart(short) {
|
|
113
|
+
const text = short?.trim();
|
|
114
|
+
return text ? `\x1b[2m◆ ${text}\x1b[22m` : '';
|
|
115
|
+
}
|
|
116
|
+
export function renderClaudeStatusLine(payload, host = os.hostname().split('.')[0] || os.hostname(), delegated = '', reminder = '') {
|
|
107
117
|
const model = payload.model?.display_name?.trim() || payload.model?.id?.trim() || 'model pending';
|
|
108
118
|
const parts = [host, model];
|
|
109
119
|
if (delegated)
|
|
@@ -114,8 +124,23 @@ export function renderClaudeStatusLine(payload, host = os.hostname().split('.')[
|
|
|
114
124
|
parts.push(`5h ${Math.round(fiveHour)}%`);
|
|
115
125
|
if (Number.isFinite(sevenDay))
|
|
116
126
|
parts.push(`7d ${Math.round(sevenDay)}%`);
|
|
127
|
+
if (reminder)
|
|
128
|
+
parts.push(reminder);
|
|
117
129
|
return parts.join(' · ');
|
|
118
130
|
}
|
|
131
|
+
/**
|
|
132
|
+
* Resolve the per-session reminder for the statusline, or '' when none is
|
|
133
|
+
* configured. A malformed reminders file is swallowed here on purpose — a broken
|
|
134
|
+
* prompt is worse than a missing reminder — while `agents reminders` surfaces it.
|
|
135
|
+
*/
|
|
136
|
+
export function resolveReminderPart(sessionId) {
|
|
137
|
+
try {
|
|
138
|
+
return formatReminderPart(pickReminderForSession(loadReminders(), sessionId)?.short);
|
|
139
|
+
}
|
|
140
|
+
catch {
|
|
141
|
+
return '';
|
|
142
|
+
}
|
|
143
|
+
}
|
|
119
144
|
export async function runClaudeStatusLine() {
|
|
120
145
|
const raw = await new Promise((resolve, reject) => {
|
|
121
146
|
let input = '';
|
|
@@ -135,7 +160,7 @@ export async function runClaudeStatusLine() {
|
|
|
135
160
|
const versionHome = versionHomeFromEnv(process.env);
|
|
136
161
|
if (versionHome)
|
|
137
162
|
ingestClaudeStatusLineUsage(payload, versionHome);
|
|
138
|
-
process.stdout.write(renderClaudeStatusLine(payload, undefined, versionHome ? renderDelegate(raw, versionHome) : ''));
|
|
163
|
+
process.stdout.write(renderClaudeStatusLine(payload, undefined, versionHome ? renderDelegate(raw, versionHome) : '', resolveReminderPart(payload.session_id)));
|
|
139
164
|
return 0;
|
|
140
165
|
}
|
|
141
166
|
export function installClaudeStatusLine(versionHome) {
|
|
@@ -36,7 +36,7 @@ import { ensureLockTarget } from '../fs-atomic.js';
|
|
|
36
36
|
import { walkForFiles } from '../fs-walk.js';
|
|
37
37
|
import { getBinaryPath, isVersionInstalled, resolveVersion, getVersionHomePath } from '../installations/versions.js';
|
|
38
38
|
import { resolveClaudeSetupToken } from '../claude-account-token.js';
|
|
39
|
-
import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, } from '../accounting/rotate.js';
|
|
39
|
+
import { getConfiguredRunStrategy, resolveRunVersion, resolveAccountVersion, rotationFailoverChain, readinessFromCandidate, formatNoHealthyAccountError, formatNoVerifiedUsageError, } from '../accounting/rotate.js';
|
|
40
40
|
import { isHeadedDeviceRole, selfConfiguredDeviceRole } from '../device-config.js';
|
|
41
41
|
import { isSelfUpdatingAgent, ROUTINE_AGENT_IDS, isAgentHardDeprecated, hardDeprecationError } from '../agents.js';
|
|
42
42
|
import { isCustomHarnessName, readProfile } from '../profiles.js';
|
|
@@ -959,12 +959,21 @@ export async function resolveRoutineLaunch(config, cwd = process.cwd(), deps = {
|
|
|
959
959
|
let version;
|
|
960
960
|
let rotation = null;
|
|
961
961
|
let exhausted;
|
|
962
|
+
let noVerifiedUsage = false;
|
|
962
963
|
try {
|
|
963
964
|
const resolved = await (deps.resolveRunVersion ?? resolveRunVersion)(agent, strategy, cwd);
|
|
964
965
|
version = resolved.version ?? undefined;
|
|
965
966
|
rotation = resolved.rotation;
|
|
966
967
|
exhausted = resolved.exhausted;
|
|
967
|
-
|
|
968
|
+
noVerifiedUsage = resolved.noVerifiedUsage ?? false;
|
|
969
|
+
if (noVerifiedUsage) {
|
|
970
|
+
// Entirely stale usage (PHNX-2526): a routine is unattended, so there is
|
|
971
|
+
// no picker to divert to — it fails loud below rather than launch on a
|
|
972
|
+
// stale number. Do NOT log `rotation.picked` as a pick; it is the refused
|
|
973
|
+
// stale candidate, kept only for the failover chain.
|
|
974
|
+
process.stderr.write(`[agents] routine ${config.name}: ${strategy} found no ${agent} account with fresh usage — refusing to route on stale data\n`);
|
|
975
|
+
}
|
|
976
|
+
else if (rotation) {
|
|
968
977
|
const label = rotation.picked.email
|
|
969
978
|
? `${rotation.picked.email} · ${agent}@${rotation.picked.version}`
|
|
970
979
|
: `${agent}@${rotation.picked.version}`;
|
|
@@ -996,6 +1005,12 @@ export async function resolveRoutineLaunch(config, cwd = process.cwd(), deps = {
|
|
|
996
1005
|
if (exhausted) {
|
|
997
1006
|
throw new Error(formatNoHealthyAccountError(agent, strategy, exhausted));
|
|
998
1007
|
}
|
|
1008
|
+
// Entirely stale usage is also NOT a "fall back to the default pin" case — the
|
|
1009
|
+
// default is exactly the account whose stale number can't be trusted. Fail the
|
|
1010
|
+
// run loud (NO_VERIFIED_USAGE) rather than hammer it every tick (PHNX-2526).
|
|
1011
|
+
if (noVerifiedUsage) {
|
|
1012
|
+
throw new Error(formatNoVerifiedUsageError(agent, strategy, rotation?.healthy ?? []));
|
|
1013
|
+
}
|
|
999
1014
|
if (!version) {
|
|
1000
1015
|
version = resolveVersion(agent, cwd) ?? undefined;
|
|
1001
1016
|
}
|
|
@@ -6,7 +6,7 @@ import type { OwnerSinkStatus } from '../channels/owner-sink.js';
|
|
|
6
6
|
import { type WindowsSshEnrollmentAudit } from './windows-ssh-enrollment.js';
|
|
7
7
|
import type { SyncStatusRow, OrphanRow } from '../drift.js';
|
|
8
8
|
import type { FetchStatusMarker } from '../auto-pull.js';
|
|
9
|
-
import type
|
|
9
|
+
import { type VersionResourceReport } from '../doctor-diff.js';
|
|
10
10
|
import type { FleetDivergence, FleetHookRuntimeState, FleetVersionSignIn } from './fleet-divergence.js';
|
|
11
11
|
export type FindingSeverity = 'critical' | 'warning';
|
|
12
12
|
/** A machine-stable class for a finding — drives {@link remediationFor} and lets
|
|
@@ -44,6 +44,24 @@ import { loginHint } from '../signin-badge.js';
|
|
|
44
44
|
import { CONFIG_ENV_ISOLATED_AGENTS } from '../installations/shims.js';
|
|
45
45
|
import { padToWidth, stringWidth } from '../text/width.js';
|
|
46
46
|
import { windowsSshEnrollmentProblem } from './windows-ssh-enrollment.js';
|
|
47
|
+
import { DOCTOR_ALL_KINDS } from '../doctor-diff.js';
|
|
48
|
+
/**
|
|
49
|
+
* Singular noun per resource kind for finding messages. A naive `kind.replace(/s$/,'')`
|
|
50
|
+
* mangles `memory` → `memor` and `mcp` → `mcp` inconsistently, so the mapping is
|
|
51
|
+
* explicit. Keyed by `DoctorKind` so a new kind fails the type check until named.
|
|
52
|
+
*/
|
|
53
|
+
const KIND_SINGULAR = {
|
|
54
|
+
commands: 'command',
|
|
55
|
+
skills: 'skill',
|
|
56
|
+
hooks: 'hook',
|
|
57
|
+
rules: 'rule',
|
|
58
|
+
mcp: 'mcp',
|
|
59
|
+
permissions: 'permission',
|
|
60
|
+
subagents: 'subagent',
|
|
61
|
+
plugins: 'plugin',
|
|
62
|
+
workflows: 'workflow',
|
|
63
|
+
memory: 'memory',
|
|
64
|
+
};
|
|
47
65
|
const AGENT_NAMES = Object.fromEntries(ALL_AGENT_IDS.map((id) => [id, AGENTS[id].name]));
|
|
48
66
|
/** Agents with NO per-version credential isolation: their login is shared across
|
|
49
67
|
* every installed version, so a "log into THIS version" remediation would be a
|
|
@@ -87,8 +105,8 @@ export const ALL_FINDING_KINDS = [
|
|
|
87
105
|
'hook-runtime-broken', // a wired hook's generated shim wrapper is missing/unusable
|
|
88
106
|
'hook-runtime-visibility-unavailable', // remote CLI cannot report generated wrapper health
|
|
89
107
|
'cli-missing', // a managed agent whose binary won't resolve
|
|
90
|
-
'missing-resource', // a missing command/skill/rule/mcp/permission/subagent
|
|
91
|
-
'content-drift', // a resource diverged from source
|
|
108
|
+
'missing-resource', // a missing command/skill/rule/mcp/permission/subagent/workflow/memory
|
|
109
|
+
'content-drift', // a resource (any synced kind) diverged from source
|
|
92
110
|
'never-synced', // installed but never synced
|
|
93
111
|
'stale', // sources changed since last sync
|
|
94
112
|
'repo-behind', // a config repo behind origin
|
|
@@ -392,8 +410,8 @@ export function buildLocalFindings(input) {
|
|
|
392
410
|
const missingPlugins = [];
|
|
393
411
|
const missingOther = [];
|
|
394
412
|
const drifted = [];
|
|
395
|
-
for (const kind of
|
|
396
|
-
const singular = kind
|
|
413
|
+
for (const kind of DOCTOR_ALL_KINDS) {
|
|
414
|
+
const singular = KIND_SINGULAR[kind];
|
|
397
415
|
for (const r of report.kinds[kind] ?? []) {
|
|
398
416
|
if (r.status === 'missing') {
|
|
399
417
|
if (kind === 'hooks')
|
|
@@ -9,21 +9,37 @@
|
|
|
9
9
|
* - missing — resolved source exists, not present in home
|
|
10
10
|
* - extra — present in home, no source in any layer
|
|
11
11
|
*
|
|
12
|
-
* Coverage
|
|
12
|
+
* Coverage — every kind `syncResourcesToVersion` writes is content-aware, so a
|
|
13
|
+
* byte change under an unchanged name is `diff`, never a false `ok` (PHNX-3504):
|
|
13
14
|
* commands, skills, hooks, rules — full content compare with source layer.
|
|
14
|
-
* mcp
|
|
15
|
+
* mcp — structural compare of the home server def vs resolved source.
|
|
16
|
+
* subagents — re-render source through the registry transform, byte-compare.
|
|
17
|
+
* workflows — layout-aware per-harness content compare (dir tree or file).
|
|
18
|
+
* memory — knowledge facts (~/.agents/memory/*.md), per-fact byte-compare.
|
|
19
|
+
* plugins — per-item content compare of the marketplace mirror.
|
|
20
|
+
* permissions — per-rule compare in the harness's native vocabulary for the
|
|
21
|
+
* representable harnesses (claude/opencode/cursor/droid/openclaw); the lossy
|
|
22
|
+
* TOML/flag harnesses stay presence-only with an honest `detail`, never a
|
|
23
|
+
* faked `ok`.
|
|
24
|
+
* A completeness test binds `DOCTOR_ALL_KINDS` to the writer set so a future
|
|
25
|
+
* synced kind cannot silently become a blind spot. `promptcuts` is NOT a kind —
|
|
26
|
+
* it is not version-scoped, so there is nothing per-home to diff.
|
|
15
27
|
*
|
|
16
28
|
* Intentional asymmetries (must mirror sync):
|
|
17
29
|
* - hooks ignore the project layer (`syncResourcesToVersion` skips
|
|
18
30
|
* project/.agents/hooks/ for safety).
|
|
19
|
-
* - rules/AGENTS.md
|
|
20
|
-
*
|
|
31
|
+
* - rules/AGENTS.md is compared against the preset composition the rules writer
|
|
32
|
+
* emits (`composeRulesFromState`), re-rendered from the current `subrules/`
|
|
33
|
+
* fragments — so a fragment edit is caught even though `rules/AGENTS.md`
|
|
34
|
+
* never changed. A non-@-import home compiled by `agents refresh-rules`
|
|
35
|
+
* carries a leading `COMPILED_HEADER`, which is stripped before comparing so
|
|
36
|
+
* a header-compiled home still reconciles (the header is not source content).
|
|
21
37
|
*/
|
|
22
38
|
import type { AgentId } from './types.js';
|
|
23
39
|
import type { DiscoveredPlugin } from './types.js';
|
|
24
40
|
import { type HookWiringReport } from './hooks/install.js';
|
|
25
41
|
import { type ResourceInventory } from './resource-inventory.js';
|
|
26
|
-
export type DoctorKind = 'commands' | 'skills' | 'hooks' | 'rules' | 'mcp' | 'permissions' | 'subagents' | 'plugins' | '
|
|
42
|
+
export type DoctorKind = 'commands' | 'skills' | 'hooks' | 'rules' | 'mcp' | 'permissions' | 'subagents' | 'plugins' | 'workflows' | 'memory';
|
|
27
43
|
export type DiffStatus = 'ok' | 'diff' | 'missing' | 'extra';
|
|
28
44
|
export type SourceLayer = 'project' | 'user' | 'system' | 'extra';
|
|
29
45
|
export interface ResourceDiff {
|