@gajae-code/coding-agent 0.18.6 → 0.18.8
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 +318 -1
- package/dist/types/async/job-manager.d.ts +1 -1
- package/dist/types/capability/mcp.d.ts +2 -0
- package/dist/types/capability/skill.d.ts +2 -0
- package/dist/types/capability/types.d.ts +6 -0
- package/dist/types/cli/public-command-errors.d.ts +3 -1
- package/dist/types/cli/setup-cli.d.ts +1 -1
- package/dist/types/commands/deep-interview.d.ts +3 -0
- package/dist/types/config/autorouting-tier-map.d.ts +348 -9
- package/dist/types/config/file-lock.d.ts +1 -0
- package/dist/types/config/model-bindings-applier.d.ts +2 -0
- package/dist/types/config/model-profile-activation.d.ts +11 -3
- package/dist/types/config/model-registry.d.ts +4 -9
- package/dist/types/config/settings.d.ts +4 -0
- package/dist/types/config/skill-settings-defaults.d.ts +2 -0
- package/dist/types/cursor-delete-path.d.ts +1 -0
- package/dist/types/cursor-mcp-name.d.ts +8 -0
- package/dist/types/customization/inventory.d.ts +2 -0
- package/dist/types/debug/report-bundle-parent.d.ts +2 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
- package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
- package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
- package/dist/types/discovery/helpers.d.ts +8 -3
- package/dist/types/export/html/css-color.d.ts +2 -0
- package/dist/types/export/html/template.generated.d.ts +1 -1
- package/dist/types/extensibility/extensions/types.d.ts +7 -0
- package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
- package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
- package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
- package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
- package/dist/types/extensibility/skill-management.d.ts +3 -1
- package/dist/types/extensibility/skills.d.ts +2 -0
- package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
- package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
- package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
- package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
- package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
- package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
- package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
- package/dist/types/gjc-runtime/state-writer.d.ts +11 -1
- package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
- package/dist/types/internal-urls/skill-path.d.ts +1 -0
- package/dist/types/lsp/workspace-path.d.ts +1 -0
- package/dist/types/modes/acp/acp-agent.d.ts +7 -0
- package/dist/types/modes/components/custom-editor.d.ts +6 -0
- package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
- package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
- package/dist/types/modes/controllers/input-controller.d.ts +9 -0
- package/dist/types/modes/interactive-mode.d.ts +1 -0
- package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
- package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
- package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
- package/dist/types/progress/collect-project-progress.d.ts +38 -0
- package/dist/types/progress/progress-contract.d.ts +154 -0
- package/dist/types/progress/progress-snapshot.d.ts +11 -0
- package/dist/types/progress/project-progress.d.ts +141 -0
- package/dist/types/progress/render-progress.d.ts +28 -0
- package/dist/types/runtime-mcp/elicitation.d.ts +12 -0
- package/dist/types/runtime-mcp/oauth-public-url.d.ts +1 -0
- package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
- package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
- package/dist/types/runtime-mcp/types.d.ts +6 -0
- package/dist/types/sdk/acp/adapter.d.ts +1 -1
- package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
- package/dist/types/sdk/broker/broker.d.ts +18 -1
- package/dist/types/sdk/broker/lifecycle.d.ts +9 -0
- package/dist/types/sdk/broker/session-index.d.ts +12 -6
- package/dist/types/sdk/broker/task-artifact-owner-validation.d.ts +55 -0
- package/dist/types/sdk/bus/index.d.ts +12 -1
- package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
- package/dist/types/sdk/bus/telegram-daemon.d.ts +3 -2
- package/dist/types/sdk/client/client.d.ts +2 -0
- package/dist/types/sdk/host/host.d.ts +6 -0
- package/dist/types/sdk/host/query/handlers.d.ts +3 -0
- package/dist/types/sdk/index.d.ts +1 -0
- package/dist/types/sdk/router/session-router.d.ts +9 -0
- package/dist/types/sdk/session-list.d.ts +1 -1
- package/dist/types/sdk/session.d.ts +8 -0
- package/dist/types/session/agent-session.d.ts +38 -9
- package/dist/types/session/artifacts.d.ts +2 -2
- package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
- package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
- package/dist/types/session/internal/managed-session-storage.d.ts +36 -1
- package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
- package/dist/types/session/internal/task-artifact-owner-access.d.ts +19 -0
- package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +11 -0
- package/dist/types/session/session-manager.d.ts +31 -5
- package/dist/types/session/session-retirement.d.ts +71 -0
- package/dist/types/session/session-storage.d.ts +62 -48
- package/dist/types/session/session-transcript-header.d.ts +2 -0
- package/dist/types/session/task-artifact-owner-codec.d.ts +109 -0
- package/dist/types/session/task-artifact-owner-retirement.d.ts +7 -0
- package/dist/types/session/task-artifact-owner.d.ts +10 -0
- package/dist/types/setup/credential-auto-import.d.ts +1 -1
- package/dist/types/setup/credential-import.d.ts +6 -4
- package/dist/types/skill-state/active-state.d.ts +38 -0
- package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
- package/dist/types/task/executor.d.ts +3 -0
- package/dist/types/tools/bash-skill-urls.d.ts +1 -1
- package/dist/types/tools/index.d.ts +3 -0
- package/dist/types/tools/python.d.ts +9 -1
- package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
- package/dist/types/tools/restricted-cwd.d.ts +1 -0
- package/dist/types/tools/todo-write.d.ts +6 -0
- package/dist/types/utils/tools-checksum.d.ts +7 -0
- package/package.json +7 -7
- package/scripts/generate-sdk-operation-inventory.ts +21 -0
- package/scripts/resident-memory-bench.ts +174 -20
- package/scripts/verify-managed-empty-stop.ts +28 -0
- package/src/async/job-manager.ts +228 -122
- package/src/capability/index.ts +18 -0
- package/src/capability/mcp.ts +2 -0
- package/src/capability/skill.ts +2 -0
- package/src/capability/types.ts +6 -0
- package/src/cli/auth-broker-cli.ts +1 -1
- package/src/cli/config-cli.ts +8 -18
- package/src/cli/customize-doctor.ts +15 -2
- package/src/cli/public-command-errors.ts +21 -1
- package/src/cli/setup-cli.ts +4 -4
- package/src/cli/skills-cli.ts +5 -0
- package/src/cli-main.ts +7 -8
- package/src/commands/deep-interview.ts +47 -8
- package/src/commands/sdk.ts +140 -29
- package/src/config/autorouting-tier-map.ts +117 -7
- package/src/config/file-lock.ts +307 -68
- package/src/config/mcp-schema.json +4 -0
- package/src/config/model-bindings-applier.ts +20 -0
- package/src/config/model-profile-activation.ts +240 -46
- package/src/config/model-registry.ts +47 -153
- package/src/config/settings.ts +44 -4
- package/src/config/skill-settings-defaults.ts +7 -0
- package/src/coordinator-mcp/policy.ts +3 -2
- package/src/cursor-delete-path.ts +10 -0
- package/src/cursor-mcp-name.ts +15 -0
- package/src/cursor.ts +13 -2
- package/src/customization/inventory.ts +3 -0
- package/src/debug/report-bundle-parent.ts +22 -0
- package/src/debug/report-bundle.ts +3 -0
- package/src/debug/system-info.ts +13 -1
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
- package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
- package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
- package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
- package/src/defaults/gjc-defaults.test.ts +18 -1
- package/src/defaults/gjc-defaults.ts +3 -0
- package/src/defaults/gjc-skills.generated.ts +58 -36
- package/src/discovery/agents.ts +6 -1
- package/src/discovery/builtin.ts +10 -0
- package/src/discovery/helpers.ts +168 -23
- package/src/discovery/mcp-json.ts +6 -0
- package/src/discovery/ssh.ts +9 -4
- package/src/discovery/substitute-plugin-root.ts +3 -1
- package/src/edit/file-read-cache.ts +1 -1
- package/src/eval/py/executor.ts +380 -120
- package/src/export/html/css-color.ts +9 -0
- package/src/export/html/index.ts +5 -4
- package/src/export/html/template.generated.ts +1 -1
- package/src/extensibility/extensions/runner.ts +4 -0
- package/src/extensibility/extensions/types.ts +7 -0
- package/src/extensibility/gjc-plugins/runtime-adapters.ts +73 -8
- package/src/extensibility/plugins/manager.ts +3 -2
- package/src/extensibility/plugins/marketplace/cache.ts +15 -0
- package/src/extensibility/plugins/marketplace/manager.ts +11 -3
- package/src/extensibility/plugins/plugin-link-path.ts +12 -0
- package/src/extensibility/runtime-skill-discovery.ts +20 -2
- package/src/extensibility/skill-management.ts +6 -2
- package/src/extensibility/skills.ts +6 -1
- package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
- package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
- package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
- package/src/gjc-runtime/deep-interview-stage.ts +98 -21
- package/src/gjc-runtime/gc-runtime.ts +206 -8
- package/src/gjc-runtime/launch-worktree.ts +14 -4
- package/src/gjc-runtime/ralplan-runtime.ts +447 -52
- package/src/gjc-runtime/state-argv.ts +2 -0
- package/src/gjc-runtime/state-migrations.ts +91 -29
- package/src/gjc-runtime/state-runtime.ts +3716 -135
- package/src/gjc-runtime/state-writer.ts +83 -8
- package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
- package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
- package/src/gjc-runtime/workflow-manifest.ts +54 -21
- package/src/harness-control-plane/operate.ts +12 -4
- package/src/harness-control-plane/owner.ts +5 -1
- package/src/hashline/parser.ts +48 -9
- package/src/hashline/recovery.ts +13 -4
- package/src/hindsight/config.ts +9 -3
- package/src/internal-urls/docs-index.generated.ts +19 -18
- package/src/internal-urls/skill-path.ts +19 -0
- package/src/internal-urls/skill-protocol.ts +2 -0
- package/src/lsp/edits.ts +6 -0
- package/src/lsp/workspace-path.ts +51 -0
- package/src/main.ts +195 -31
- package/src/modes/acp/acp-agent.ts +163 -7
- package/src/modes/components/custom-editor.ts +14 -0
- package/src/modes/components/customization/customization-dashboard.ts +12 -0
- package/src/modes/components/extensions/extension-dashboard.ts +2 -2
- package/src/modes/components/extensions/state-manager.ts +15 -5
- package/src/modes/components/model-selector.ts +1 -4
- package/src/modes/components/provider-onboarding-selector.ts +1 -1
- package/src/modes/components/status-line/segments.ts +4 -0
- package/src/modes/controllers/event-controller.ts +11 -4
- package/src/modes/controllers/input-controller.ts +74 -29
- package/src/modes/controllers/runtime-mcp-command-controller.ts +8 -3
- package/src/modes/controllers/selector-controller.ts +28 -15
- package/src/modes/interactive-mode.ts +4 -0
- package/src/modes/runtime-init.ts +4 -0
- package/src/modes/utils/abort-message.ts +3 -1
- package/src/modes/utils/interrupt-ownership.ts +12 -0
- package/src/modes/utils/keybinding-matchers.ts +4 -0
- package/src/modes/utils/macos-option-forwarding.ts +80 -0
- package/src/progress/collect-project-progress.ts +153 -0
- package/src/progress/progress-contract.ts +172 -0
- package/src/progress/progress-snapshot.ts +140 -0
- package/src/progress/project-progress.ts +446 -0
- package/src/progress/render-progress.ts +209 -0
- package/src/prompts/system/master-mode.md +1 -0
- package/src/prompts/tools/skill-discovery.md +1 -1
- package/src/runtime-mcp/client.ts +13 -4
- package/src/runtime-mcp/config-writer.ts +10 -6
- package/src/runtime-mcp/config.ts +1 -0
- package/src/runtime-mcp/elicitation.ts +142 -0
- package/src/runtime-mcp/oauth-flow.ts +5 -0
- package/src/runtime-mcp/oauth-public-url.ts +8 -0
- package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
- package/src/runtime-mcp/smithery-registry.ts +22 -3
- package/src/runtime-mcp/transports/http.ts +2 -2
- package/src/runtime-mcp/types.ts +6 -0
- package/src/sdk/acp/adapter.ts +93 -8
- package/src/sdk/broker/broker-exit.ts +52 -23
- package/src/sdk/broker/broker.ts +83 -7
- package/src/sdk/broker/ensure.ts +2 -0
- package/src/sdk/broker/lifecycle.ts +820 -73
- package/src/sdk/broker/session-index.ts +191 -81
- package/src/sdk/broker/task-artifact-owner-validation.ts +259 -0
- package/src/sdk/bus/chat-daemon-cli.ts +38 -1
- package/src/sdk/bus/chat-daemon-control.ts +2 -2
- package/src/sdk/bus/chat-daemon-runtime.ts +3 -0
- package/src/sdk/bus/index.ts +241 -46
- package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
- package/src/sdk/bus/telegram-daemon.ts +4 -2
- package/src/sdk/cli/session-cli.ts +17 -3
- package/src/sdk/client/client.ts +28 -8
- package/src/sdk/host/host.ts +6 -0
- package/src/sdk/host/query/handlers.ts +9 -1
- package/src/sdk/host/session-runtime.test.ts +2805 -411
- package/src/sdk/host/session-runtime.ts +428 -111
- package/src/sdk/host/surface-policy.ts +1 -0
- package/src/sdk/index.ts +8 -0
- package/src/sdk/lifecycle-session.ts +3 -1
- package/src/sdk/mcp/server.ts +15 -3
- package/src/sdk/prompt-deadline-manager.ts +15 -0
- package/src/sdk/protocol/operation-inventory.generated.json +146 -0
- package/src/sdk/protocol/operation-registry.ts +9 -2
- package/src/sdk/router/session-router.ts +94 -14
- package/src/sdk/session-list.ts +1 -0
- package/src/sdk/session.ts +81 -38
- package/src/session/agent-session.ts +674 -224
- package/src/session/artifacts.ts +174 -70
- package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
- package/src/session/internal/managed-session-scope.ts +3457 -462
- package/src/session/internal/managed-session-storage.ts +482 -61
- package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
- package/src/session/internal/task-artifact-owner-access.ts +245 -0
- package/src/session/internal/task-artifact-owner-transcript.ts +614 -0
- package/src/session/session-manager.ts +939 -133
- package/src/session/session-retirement.ts +714 -0
- package/src/session/session-storage.ts +418 -174
- package/src/session/session-transcript-header.ts +17 -0
- package/src/session/task-artifact-owner-codec.ts +641 -0
- package/src/session/task-artifact-owner-retirement.ts +477 -0
- package/src/session/task-artifact-owner.ts +289 -0
- package/src/setup/credential-auto-import.ts +12 -3
- package/src/setup/credential-import.ts +176 -5
- package/src/setup/paseo/announce.ts +6 -1
- package/src/skill-state/active-state.ts +132 -2
- package/src/slash-commands/builtin-registry.ts +39 -9
- package/src/slash-commands/helpers/progress-report.ts +23 -0
- package/src/stt/setup.ts +3 -5
- package/src/task/commands.ts +3 -1
- package/src/task/executor.ts +4 -0
- package/src/task/index.ts +6 -0
- package/src/tools/ask.ts +205 -6
- package/src/tools/ast-edit.ts +4 -0
- package/src/tools/bash-allowed-prefixes.ts +15 -0
- package/src/tools/bash-skill-urls.ts +56 -3
- package/src/tools/bash.ts +48 -79
- package/src/tools/descriptors.ts +5 -1
- package/src/tools/eval.ts +56 -36
- package/src/tools/image-gen.ts +1 -1
- package/src/tools/index.ts +3 -0
- package/src/tools/python.ts +187 -66
- package/src/tools/recipe/index.ts +9 -0
- package/src/tools/recipe/recipe-guard.ts +19 -0
- package/src/tools/restricted-cwd.ts +10 -0
- package/src/tools/skill-discovery.ts +5 -0
- package/src/tools/skill.ts +28 -1
- package/src/tools/todo-write.ts +10 -1
- package/src/tools/tool-catalog.generated.ts +1 -1
- package/src/tools/write.ts +1 -0
- package/src/utils/tools-checksum.ts +34 -0
- package/src/utils/tools-manager.ts +24 -6
- package/src/web/search/providers/codex.ts +3 -2
|
@@ -1,9 +1,34 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Generated bundled GJC workflow skill catalog.
|
|
3
3
|
*
|
|
4
|
-
* Keep this module metadata-only: skill bodies are
|
|
5
|
-
*
|
|
4
|
+
* Keep this module metadata-only: skill bodies are read from their embedded
|
|
5
|
+
* `type: "file"` paths only when a caller asks for their content. Do not also
|
|
6
|
+
* import a body with `type: "text"`: Bun caches a module per specifier, so the
|
|
7
|
+
* second import of the same file would return the first one's value.
|
|
6
8
|
*/
|
|
9
|
+
import { readFile } from "node:fs/promises";
|
|
10
|
+
import autoresearchAutoCriticPath from "./gjc/skills/autoresearch/auto-critic.md" with { type: "file" };
|
|
11
|
+
import autoresearchAutoIteratePath from "./gjc/skills/autoresearch/auto-iterate.md" with { type: "file" };
|
|
12
|
+
import autoresearchSkillPath from "./gjc/skills/autoresearch/SKILL.md" with { type: "file" };
|
|
13
|
+
import deepInterviewAutoAnswerUncertainPath from "./gjc/skills/deep-interview/auto-answer-uncertain.md" with {
|
|
14
|
+
type: "file",
|
|
15
|
+
};
|
|
16
|
+
import deepInterviewLateralReviewPanelPath from "./gjc/skills/deep-interview/lateral-review-panel.md" with {
|
|
17
|
+
type: "file",
|
|
18
|
+
};
|
|
19
|
+
import deepInterviewSkillPath from "./gjc/skills/deep-interview/SKILL.md" with { type: "file" };
|
|
20
|
+
import ralplanSkillPath from "./gjc/skills/ralplan/SKILL.md" with { type: "file" };
|
|
21
|
+
import ultragoalAiSlopCleanerPath from "./gjc/skills/ultragoal/ai-slop-cleaner.md" with { type: "file" };
|
|
22
|
+
import ultragoalBoundaryCohortGatePath from "./gjc/skills/ultragoal/boundary-cohort-gate.md" with { type: "file" };
|
|
23
|
+
import ultragoalCrossRepositorySuccessionPath from "./gjc/skills/ultragoal/cross-repository-succession.md" with {
|
|
24
|
+
type: "file",
|
|
25
|
+
};
|
|
26
|
+
import ultragoalSkillPath from "./gjc/skills/ultragoal/SKILL.md" with { type: "file" };
|
|
27
|
+
import ultragoalTerminalCriticGatePath from "./gjc/skills/ultragoal/terminal-critic-gate.md" with { type: "file" };
|
|
28
|
+
import ultragoalValidationBatchContractsPath from "./gjc/skills/ultragoal/validation-batch-contracts.md" with {
|
|
29
|
+
type: "file",
|
|
30
|
+
};
|
|
31
|
+
|
|
7
32
|
export type BundledGjcSkillName = "autoresearch" | "deep-interview" | "ralplan" | "ultragoal";
|
|
8
33
|
|
|
9
34
|
export interface BundledGjcSkillCatalogEntry {
|
|
@@ -12,49 +37,34 @@ export interface BundledGjcSkillCatalogEntry {
|
|
|
12
37
|
readonly parentSkillName?: BundledGjcSkillName;
|
|
13
38
|
readonly relativePath: string;
|
|
14
39
|
readonly description?: string;
|
|
40
|
+
/**
|
|
41
|
+
* Path of the bundled body. A `type: "file"` import, so `bun build --compile`
|
|
42
|
+
* embeds the file and this resolves inside `/$bunfs` in compiled binaries.
|
|
43
|
+
*/
|
|
44
|
+
readonly sourcePath?: string;
|
|
15
45
|
readonly loadContent: () => Promise<string>;
|
|
16
46
|
}
|
|
17
47
|
|
|
18
|
-
const deepInterview = () =>
|
|
19
|
-
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
import("./gjc/skills/deep-interview/lateral-review-panel.md", { with: { type: "text" } }).then(
|
|
32
|
-
module => module.default,
|
|
33
|
-
);
|
|
34
|
-
const aiSlopCleaner = () =>
|
|
35
|
-
import("./gjc/skills/ultragoal/ai-slop-cleaner.md", { with: { type: "text" } }).then(module => module.default);
|
|
36
|
-
const validationBatchContracts = () =>
|
|
37
|
-
import("./gjc/skills/ultragoal/validation-batch-contracts.md", { with: { type: "text" } }).then(
|
|
38
|
-
module => module.default,
|
|
39
|
-
);
|
|
40
|
-
const boundaryCohortGate = () =>
|
|
41
|
-
import("./gjc/skills/ultragoal/boundary-cohort-gate.md", { with: { type: "text" } }).then(module => module.default);
|
|
42
|
-
const terminalCriticGate = () =>
|
|
43
|
-
import("./gjc/skills/ultragoal/terminal-critic-gate.md", { with: { type: "text" } }).then(module => module.default);
|
|
44
|
-
const crossRepositorySuccession = () =>
|
|
45
|
-
import("./gjc/skills/ultragoal/cross-repository-succession.md", { with: { type: "text" } }).then(
|
|
46
|
-
module => module.default,
|
|
47
|
-
);
|
|
48
|
-
const autoresearchIterate = () =>
|
|
49
|
-
import("./gjc/skills/autoresearch/auto-iterate.md", { with: { type: "text" } }).then(module => module.default);
|
|
50
|
-
const autoresearchCritic = () =>
|
|
51
|
-
import("./gjc/skills/autoresearch/auto-critic.md", { with: { type: "text" } }).then(module => module.default);
|
|
48
|
+
const deepInterview = () => readFile(deepInterviewSkillPath, "utf8");
|
|
49
|
+
const ralplan = () => readFile(ralplanSkillPath, "utf8");
|
|
50
|
+
const autoresearch = () => readFile(autoresearchSkillPath, "utf8");
|
|
51
|
+
const ultragoal = () => readFile(ultragoalSkillPath, "utf8");
|
|
52
|
+
const autoAnswerUncertain = () => readFile(deepInterviewAutoAnswerUncertainPath, "utf8");
|
|
53
|
+
const lateralReviewPanel = () => readFile(deepInterviewLateralReviewPanelPath, "utf8");
|
|
54
|
+
const aiSlopCleaner = () => readFile(ultragoalAiSlopCleanerPath, "utf8");
|
|
55
|
+
const validationBatchContracts = () => readFile(ultragoalValidationBatchContractsPath, "utf8");
|
|
56
|
+
const boundaryCohortGate = () => readFile(ultragoalBoundaryCohortGatePath, "utf8");
|
|
57
|
+
const terminalCriticGate = () => readFile(ultragoalTerminalCriticGatePath, "utf8");
|
|
58
|
+
const crossRepositorySuccession = () => readFile(ultragoalCrossRepositorySuccessionPath, "utf8");
|
|
59
|
+
const autoresearchIterate = () => readFile(autoresearchAutoIteratePath, "utf8");
|
|
60
|
+
const autoresearchCritic = () => readFile(autoresearchAutoCriticPath, "utf8");
|
|
52
61
|
|
|
53
62
|
export const BUNDLED_GJC_SKILL_CATALOG: readonly BundledGjcSkillCatalogEntry[] = [
|
|
54
63
|
{
|
|
55
64
|
kind: "skill",
|
|
56
65
|
name: "deep-interview",
|
|
57
66
|
relativePath: "skills/deep-interview/SKILL.md",
|
|
67
|
+
sourcePath: deepInterviewSkillPath,
|
|
58
68
|
description: "Socratic deep interview with mathematical ambiguity gating before explicit execution approval",
|
|
59
69
|
loadContent: deepInterview,
|
|
60
70
|
},
|
|
@@ -62,6 +72,7 @@ export const BUNDLED_GJC_SKILL_CATALOG: readonly BundledGjcSkillCatalogEntry[] =
|
|
|
62
72
|
kind: "skill",
|
|
63
73
|
name: "ralplan",
|
|
64
74
|
relativePath: "skills/ralplan/SKILL.md",
|
|
75
|
+
sourcePath: ralplanSkillPath,
|
|
65
76
|
description: "Consensus planning entrypoint that auto-gates vague ultragoal requests before execution",
|
|
66
77
|
loadContent: ralplan,
|
|
67
78
|
},
|
|
@@ -69,6 +80,7 @@ export const BUNDLED_GJC_SKILL_CATALOG: readonly BundledGjcSkillCatalogEntry[] =
|
|
|
69
80
|
kind: "skill",
|
|
70
81
|
name: "autoresearch",
|
|
71
82
|
relativePath: "skills/autoresearch/SKILL.md",
|
|
83
|
+
sourcePath: autoresearchSkillPath,
|
|
72
84
|
description: "Goal-directed research missions interleaving web and data evidence into a structured verdict",
|
|
73
85
|
loadContent: autoresearch,
|
|
74
86
|
},
|
|
@@ -76,6 +88,7 @@ export const BUNDLED_GJC_SKILL_CATALOG: readonly BundledGjcSkillCatalogEntry[] =
|
|
|
76
88
|
kind: "skill",
|
|
77
89
|
name: "ultragoal",
|
|
78
90
|
relativePath: "skills/ultragoal/SKILL.md",
|
|
91
|
+
sourcePath: ultragoalSkillPath,
|
|
79
92
|
description: "Create and execute durable repo-native multi-goal plans over GJC goal mode artifacts.",
|
|
80
93
|
loadContent: ultragoal,
|
|
81
94
|
},
|
|
@@ -83,54 +96,63 @@ export const BUNDLED_GJC_SKILL_CATALOG: readonly BundledGjcSkillCatalogEntry[] =
|
|
|
83
96
|
kind: "skill-fragment",
|
|
84
97
|
parentSkillName: "deep-interview",
|
|
85
98
|
relativePath: "skill-fragments/deep-interview/auto-answer-uncertain.md",
|
|
99
|
+
sourcePath: deepInterviewAutoAnswerUncertainPath,
|
|
86
100
|
loadContent: autoAnswerUncertain,
|
|
87
101
|
},
|
|
88
102
|
{
|
|
89
103
|
kind: "skill-fragment",
|
|
90
104
|
parentSkillName: "deep-interview",
|
|
91
105
|
relativePath: "skill-fragments/deep-interview/lateral-review-panel.md",
|
|
106
|
+
sourcePath: deepInterviewLateralReviewPanelPath,
|
|
92
107
|
loadContent: lateralReviewPanel,
|
|
93
108
|
},
|
|
94
109
|
{
|
|
95
110
|
kind: "skill-fragment",
|
|
96
111
|
parentSkillName: "ultragoal",
|
|
97
112
|
relativePath: "skill-fragments/ultragoal/ai-slop-cleaner.md",
|
|
113
|
+
sourcePath: ultragoalAiSlopCleanerPath,
|
|
98
114
|
loadContent: aiSlopCleaner,
|
|
99
115
|
},
|
|
100
116
|
{
|
|
101
117
|
kind: "skill-fragment",
|
|
102
118
|
parentSkillName: "ultragoal",
|
|
103
119
|
relativePath: "skill-fragments/ultragoal/validation-batch-contracts.md",
|
|
120
|
+
sourcePath: ultragoalValidationBatchContractsPath,
|
|
104
121
|
loadContent: validationBatchContracts,
|
|
105
122
|
},
|
|
106
123
|
{
|
|
107
124
|
kind: "skill-fragment",
|
|
108
125
|
parentSkillName: "ultragoal",
|
|
109
126
|
relativePath: "skill-fragments/ultragoal/boundary-cohort-gate.md",
|
|
127
|
+
sourcePath: ultragoalBoundaryCohortGatePath,
|
|
110
128
|
loadContent: boundaryCohortGate,
|
|
111
129
|
},
|
|
112
130
|
{
|
|
113
131
|
kind: "skill-fragment",
|
|
114
132
|
parentSkillName: "ultragoal",
|
|
115
133
|
relativePath: "skill-fragments/ultragoal/terminal-critic-gate.md",
|
|
134
|
+
sourcePath: ultragoalTerminalCriticGatePath,
|
|
116
135
|
loadContent: terminalCriticGate,
|
|
117
136
|
},
|
|
118
137
|
{
|
|
119
138
|
kind: "skill-fragment",
|
|
120
139
|
parentSkillName: "ultragoal",
|
|
121
140
|
relativePath: "skill-fragments/ultragoal/cross-repository-succession.md",
|
|
141
|
+
sourcePath: ultragoalCrossRepositorySuccessionPath,
|
|
122
142
|
loadContent: crossRepositorySuccession,
|
|
123
143
|
},
|
|
124
144
|
{
|
|
125
145
|
kind: "skill-fragment",
|
|
126
146
|
parentSkillName: "autoresearch",
|
|
127
147
|
relativePath: "skill-fragments/autoresearch/auto-iterate.md",
|
|
148
|
+
sourcePath: autoresearchAutoIteratePath,
|
|
128
149
|
loadContent: autoresearchIterate,
|
|
129
150
|
},
|
|
130
151
|
{
|
|
131
152
|
kind: "skill-fragment",
|
|
132
153
|
parentSkillName: "autoresearch",
|
|
133
154
|
relativePath: "skill-fragments/autoresearch/auto-critic.md",
|
|
155
|
+
sourcePath: autoresearchAutoCriticPath,
|
|
134
156
|
loadContent: autoresearchCritic,
|
|
135
157
|
},
|
|
136
158
|
];
|
package/src/discovery/agents.ts
CHANGED
|
@@ -79,7 +79,12 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
79
79
|
scanSkillsFromDir(ctx, { dir, providerId: PROVIDER_ID, level: "project" }),
|
|
80
80
|
);
|
|
81
81
|
const userScans = getUserPathCandidates(ctx, "skills").map(dir =>
|
|
82
|
-
scanSkillsFromDir(ctx, {
|
|
82
|
+
scanSkillsFromDir(ctx, {
|
|
83
|
+
dir,
|
|
84
|
+
providerId: PROVIDER_ID,
|
|
85
|
+
level: "user",
|
|
86
|
+
allowExternalUserSkillSymlinks: ctx.allowExternalUserSkillSymlinks,
|
|
87
|
+
}),
|
|
83
88
|
);
|
|
84
89
|
|
|
85
90
|
const results = await Promise.all([...projectScans, ...userScans]);
|
package/src/discovery/builtin.ts
CHANGED
|
@@ -22,6 +22,7 @@ import { type SlashCommand, slashCommandCapability } from "../capability/slash-c
|
|
|
22
22
|
import { type SystemPrompt, systemPromptCapability } from "../capability/system-prompt";
|
|
23
23
|
import { type CustomTool, toolCapability } from "../capability/tool";
|
|
24
24
|
import type { LoadContext, LoadResult } from "../capability/types";
|
|
25
|
+
import { resolveGlobalUserSkillLinkTrust } from "../config/skill-settings-defaults";
|
|
25
26
|
import { expandTilde } from "../tools/path-utils";
|
|
26
27
|
import {
|
|
27
28
|
buildRuleFromMarkdown,
|
|
@@ -220,6 +221,7 @@ async function loadMCPServers(ctx: LoadContext): Promise<LoadResult<MCPServer>>
|
|
|
220
221
|
cwd: serverConfig.cwd as string | undefined,
|
|
221
222
|
url: serverConfig.url as string | undefined,
|
|
222
223
|
headers: serverConfig.headers as Record<string, string> | undefined,
|
|
224
|
+
publicNetwork: serverConfig.publicNetwork === true ? true : undefined,
|
|
223
225
|
auth: serverConfig.auth as
|
|
224
226
|
| {
|
|
225
227
|
type: "oauth" | "apikey";
|
|
@@ -359,6 +361,14 @@ async function loadSkills(ctx: LoadContext): Promise<LoadResult<Skill>> {
|
|
|
359
361
|
level: "user",
|
|
360
362
|
scope: "native",
|
|
361
363
|
requireDescription: true,
|
|
364
|
+
allowExternalUserSkillSymlinks:
|
|
365
|
+
ctx.allowExternalUserSkillSymlinks ??
|
|
366
|
+
(typeof ctx.settings?.getGlobal === "function"
|
|
367
|
+
? resolveGlobalUserSkillLinkTrust({
|
|
368
|
+
trustUserSkills: ctx.settings.getGlobal("skills.trustUserSkills"),
|
|
369
|
+
enablePiUser: ctx.settings.getGlobal("skills.enablePiUser"),
|
|
370
|
+
})
|
|
371
|
+
: true),
|
|
362
372
|
}),
|
|
363
373
|
);
|
|
364
374
|
|
package/src/discovery/helpers.ts
CHANGED
|
@@ -203,10 +203,13 @@ export function getProjectPath(ctx: LoadContext, source: SourceId, subpath: stri
|
|
|
203
203
|
|
|
204
204
|
/** Build the filesystem authority for a provider read. */
|
|
205
205
|
export function getReadOptions(
|
|
206
|
-
ctx: Pick<
|
|
206
|
+
ctx: Pick<
|
|
207
|
+
LoadContext,
|
|
208
|
+
"home" | "isolatedHome" | "userAgentDir" | "homeIdentity" | "userAgentIdentity" | "bypassCache"
|
|
209
|
+
>,
|
|
207
210
|
scope: ReadScope,
|
|
208
211
|
): ReadFileOptions | undefined {
|
|
209
|
-
if (!ctx.isolatedHome) return undefined;
|
|
212
|
+
if (!ctx.isolatedHome) return ctx.bypassCache ? { bypassCache: true } : undefined;
|
|
210
213
|
return {
|
|
211
214
|
isolatedHome: true,
|
|
212
215
|
home: ctx.home,
|
|
@@ -219,7 +222,10 @@ export function getReadOptions(
|
|
|
219
222
|
}
|
|
220
223
|
|
|
221
224
|
export async function getReadOptionsForContainment(
|
|
222
|
-
ctx: Pick<
|
|
225
|
+
ctx: Pick<
|
|
226
|
+
LoadContext,
|
|
227
|
+
"home" | "isolatedHome" | "userAgentDir" | "homeIdentity" | "userAgentIdentity" | "bypassCache"
|
|
228
|
+
>,
|
|
223
229
|
scope: ReadScope,
|
|
224
230
|
containmentRoot?: string,
|
|
225
231
|
): Promise<ReadFileOptions | undefined> {
|
|
@@ -473,6 +479,8 @@ export interface ScanSkillsFromDirOptions {
|
|
|
473
479
|
linkContainmentRoot?: string;
|
|
474
480
|
/** Filesystem authority for explicit-home reads. */
|
|
475
481
|
scope?: ReadScope;
|
|
482
|
+
/** User-owned permission to follow symlinks outside a user-scope scan root. */
|
|
483
|
+
allowExternalUserSkillSymlinks?: boolean;
|
|
476
484
|
}
|
|
477
485
|
|
|
478
486
|
// Stable ordering used for skill lists in prompts: name (case-insensitive), then name, then path.
|
|
@@ -492,6 +500,8 @@ export const SKILL_FRONTMATTER_SCAN_TOTAL_BYTES = 64 * 1024;
|
|
|
492
500
|
|
|
493
501
|
export const SkillDiscoveryTestHooks: {
|
|
494
502
|
afterCandidateValidated?: (candidatePath: string) => void | Promise<void>;
|
|
503
|
+
afterSkillLinksCaptured?: (candidatePath: string) => void | Promise<void>;
|
|
504
|
+
afterSkillPathResolved?: (candidatePath: string) => void | Promise<void>;
|
|
495
505
|
afterAuthorityRootValidated?: (root: string) => void | Promise<void>;
|
|
496
506
|
afterAuthorityRootMissing?: (root: string) => void | Promise<void>;
|
|
497
507
|
afterContainedRootValidated?: (root: string) => void | Promise<void>;
|
|
@@ -511,6 +521,25 @@ interface ScanRootLinkIdentity {
|
|
|
511
521
|
target: DirectoryIdentity;
|
|
512
522
|
}
|
|
513
523
|
|
|
524
|
+
interface SkillFileIdentity {
|
|
525
|
+
dev: bigint;
|
|
526
|
+
ino: bigint;
|
|
527
|
+
realPath: string;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
interface SkillLinkIdentity {
|
|
531
|
+
path: string;
|
|
532
|
+
dev: bigint;
|
|
533
|
+
ino: bigint;
|
|
534
|
+
linkText: string;
|
|
535
|
+
target: {
|
|
536
|
+
dev: bigint;
|
|
537
|
+
ino: bigint;
|
|
538
|
+
realPath: string;
|
|
539
|
+
kind: "directory" | "file";
|
|
540
|
+
};
|
|
541
|
+
}
|
|
542
|
+
|
|
514
543
|
async function captureScanRootLinkIdentity(root: string): Promise<ScanRootLinkIdentity | null> {
|
|
515
544
|
const observed = await fs.promises.lstat(root, { bigint: true });
|
|
516
545
|
if (!observed.isSymbolicLink()) return null;
|
|
@@ -571,6 +600,38 @@ async function directoryIdentityIsCurrent(
|
|
|
571
600
|
}
|
|
572
601
|
}
|
|
573
602
|
|
|
603
|
+
async function captureSkillLinkIdentity(linkPath: string): Promise<SkillLinkIdentity | null> {
|
|
604
|
+
const observed = await fs.promises.lstat(linkPath, { bigint: true });
|
|
605
|
+
if (!observed.isSymbolicLink()) return null;
|
|
606
|
+
const linkText = await fs.promises.readlink(linkPath);
|
|
607
|
+
const realPath = await fs.promises.realpath(linkPath);
|
|
608
|
+
const target = await fs.promises.stat(realPath, { bigint: true });
|
|
609
|
+
const kind = target.isDirectory() ? "directory" : target.isFile() ? "file" : undefined;
|
|
610
|
+
if (!kind) return null;
|
|
611
|
+
return {
|
|
612
|
+
path: linkPath,
|
|
613
|
+
dev: observed.dev,
|
|
614
|
+
ino: observed.ino,
|
|
615
|
+
linkText,
|
|
616
|
+
target: { dev: target.dev, ino: target.ino, realPath, kind },
|
|
617
|
+
};
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
async function skillLinkIdentityIsCurrent(expected: SkillLinkIdentity): Promise<boolean> {
|
|
621
|
+
try {
|
|
622
|
+
const observed = await fs.promises.lstat(expected.path, { bigint: true });
|
|
623
|
+
if (!observed.isSymbolicLink() || observed.dev !== expected.dev || observed.ino !== expected.ino) return false;
|
|
624
|
+
if ((await fs.promises.readlink(expected.path)) !== expected.linkText) return false;
|
|
625
|
+
const realPath = await fs.promises.realpath(expected.path);
|
|
626
|
+
if (normalizePathForComparison(realPath) !== normalizePathForComparison(expected.target.realPath)) return false;
|
|
627
|
+
const target = await fs.promises.stat(realPath, { bigint: true });
|
|
628
|
+
const kind = target.isDirectory() ? "directory" : target.isFile() ? "file" : undefined;
|
|
629
|
+
return kind === expected.target.kind && target.dev === expected.target.dev && target.ino === expected.target.ino;
|
|
630
|
+
} catch {
|
|
631
|
+
return false;
|
|
632
|
+
}
|
|
633
|
+
}
|
|
634
|
+
|
|
574
635
|
export async function scanSkillsFromDir(
|
|
575
636
|
_ctx: LoadContext,
|
|
576
637
|
options: ScanSkillsFromDirOptions,
|
|
@@ -687,7 +748,36 @@ export async function scanSkillsFromDir(
|
|
|
687
748
|
const relative = path.relative(realRoot, candidate);
|
|
688
749
|
return relative === "" || (!relative.startsWith("..") && !path.isAbsolute(relative));
|
|
689
750
|
};
|
|
690
|
-
const
|
|
751
|
+
const skillPathAndLinksAreCurrent = async (
|
|
752
|
+
skillPath: string,
|
|
753
|
+
expectedFile: SkillFileIdentity | undefined,
|
|
754
|
+
linkIdentities: readonly SkillLinkIdentity[],
|
|
755
|
+
): Promise<boolean> => {
|
|
756
|
+
try {
|
|
757
|
+
const [realPath, currentFile, currentLinks] = await Promise.all([
|
|
758
|
+
fs.promises.realpath(skillPath),
|
|
759
|
+
expectedFile ? fs.promises.stat(expectedFile.realPath, { bigint: true }) : Promise.resolve(undefined),
|
|
760
|
+
Promise.all(linkIdentities.map(skillLinkIdentityIsCurrent)),
|
|
761
|
+
]);
|
|
762
|
+
if (!expectedFile) return isWithinRoot(realPath) && currentLinks.every(Boolean);
|
|
763
|
+
if (!currentFile) return false;
|
|
764
|
+
return (
|
|
765
|
+
normalizePathForComparison(realPath) === normalizePathForComparison(expectedFile.realPath) &&
|
|
766
|
+
currentFile.isFile() &&
|
|
767
|
+
currentFile.nlink === 1n &&
|
|
768
|
+
currentFile.dev === expectedFile.dev &&
|
|
769
|
+
currentFile.ino === expectedFile.ino &&
|
|
770
|
+
currentLinks.every(Boolean)
|
|
771
|
+
);
|
|
772
|
+
} catch {
|
|
773
|
+
return false;
|
|
774
|
+
}
|
|
775
|
+
};
|
|
776
|
+
const openSkillFileSafely = async (
|
|
777
|
+
skillPath: string,
|
|
778
|
+
expectedFile: SkillFileIdentity | undefined,
|
|
779
|
+
linkIdentities: readonly SkillLinkIdentity[],
|
|
780
|
+
): Promise<FileHandle> => {
|
|
691
781
|
if (
|
|
692
782
|
!(await scanRootIsCurrent()) ||
|
|
693
783
|
(options.authorityRoot &&
|
|
@@ -701,14 +791,14 @@ export async function scanSkillsFromDir(
|
|
|
701
791
|
(process.platform === "win32" ? 0 : (fs.constants.O_NOFOLLOW ?? 0) | (fs.constants.O_NONBLOCK ?? 0));
|
|
702
792
|
const handle = await fs.promises.open(skillPath, flags);
|
|
703
793
|
try {
|
|
704
|
-
const [opened, observed,
|
|
794
|
+
const [opened, observed, currentScanRoot, currentAuthorityRoot, currentSkillPath] = await Promise.all([
|
|
705
795
|
handle.stat({ bigint: true }),
|
|
706
796
|
fs.promises.lstat(skillPath, { bigint: true }),
|
|
707
|
-
fs.promises.realpath(skillPath),
|
|
708
797
|
scanRootIsCurrent(),
|
|
709
798
|
options.authorityRoot && authorityIdentity
|
|
710
799
|
? directoryIdentityIsCurrent(options.authorityRoot, authorityIdentity)
|
|
711
800
|
: true,
|
|
801
|
+
skillPathAndLinksAreCurrent(skillPath, expectedFile, linkIdentities),
|
|
712
802
|
]);
|
|
713
803
|
if (
|
|
714
804
|
!opened.isFile() ||
|
|
@@ -717,9 +807,10 @@ export async function scanSkillsFromDir(
|
|
|
717
807
|
!observed.isFile() ||
|
|
718
808
|
opened.dev !== observed.dev ||
|
|
719
809
|
opened.ino !== observed.ino ||
|
|
720
|
-
|
|
810
|
+
(expectedFile !== undefined && (opened.dev !== expectedFile.dev || opened.ino !== expectedFile.ino)) ||
|
|
721
811
|
!currentScanRoot ||
|
|
722
|
-
!currentAuthorityRoot
|
|
812
|
+
!currentAuthorityRoot ||
|
|
813
|
+
!currentSkillPath
|
|
723
814
|
) {
|
|
724
815
|
throw new Error(`Unsafe skill file: ${skillPath}`);
|
|
725
816
|
}
|
|
@@ -729,15 +820,20 @@ export async function scanSkillsFromDir(
|
|
|
729
820
|
throw error;
|
|
730
821
|
}
|
|
731
822
|
};
|
|
732
|
-
const readSkillContentSafely = async (
|
|
733
|
-
|
|
823
|
+
const readSkillContentSafely = async (
|
|
824
|
+
skillPath: string,
|
|
825
|
+
expectedFile: SkillFileIdentity | undefined,
|
|
826
|
+
linkIdentities: readonly SkillLinkIdentity[],
|
|
827
|
+
): Promise<string> => {
|
|
828
|
+
const handle = await openSkillFileSafely(skillPath, expectedFile, linkIdentities);
|
|
734
829
|
try {
|
|
735
830
|
const content = await handle.readFile({ encoding: "utf8" });
|
|
736
831
|
if (
|
|
737
832
|
!(await scanRootIsCurrent()) ||
|
|
738
833
|
(options.authorityRoot &&
|
|
739
834
|
authorityIdentity &&
|
|
740
|
-
!(await directoryIdentityIsCurrent(options.authorityRoot, authorityIdentity)))
|
|
835
|
+
!(await directoryIdentityIsCurrent(options.authorityRoot, authorityIdentity))) ||
|
|
836
|
+
!(await skillPathAndLinksAreCurrent(skillPath, expectedFile, linkIdentities))
|
|
741
837
|
) {
|
|
742
838
|
throw new Error(`Unsafe skill authority root for: ${skillPath}`);
|
|
743
839
|
}
|
|
@@ -748,8 +844,10 @@ export async function scanSkillsFromDir(
|
|
|
748
844
|
};
|
|
749
845
|
const readSkillFrontmatterSafely = async (
|
|
750
846
|
skillPath: string,
|
|
847
|
+
expectedFile: SkillFileIdentity | undefined,
|
|
848
|
+
linkIdentities: readonly SkillLinkIdentity[],
|
|
751
849
|
): Promise<{ frontmatter: SkillFrontmatter | null; size: number }> => {
|
|
752
|
-
const handle = await openSkillFileSafely(skillPath);
|
|
850
|
+
const handle = await openSkillFileSafely(skillPath, expectedFile, linkIdentities);
|
|
753
851
|
try {
|
|
754
852
|
const size = Number((await handle.stat({ bigint: true })).size);
|
|
755
853
|
const scanLimit = Math.min(size, SKILL_FRONTMATTER_SCAN_TOTAL_BYTES);
|
|
@@ -782,18 +880,55 @@ export async function scanSkillsFromDir(
|
|
|
782
880
|
};
|
|
783
881
|
const loadSkill = async (candidatePath: string) => {
|
|
784
882
|
try {
|
|
883
|
+
const linkIdentities = (
|
|
884
|
+
await Promise.all([
|
|
885
|
+
captureSkillLinkIdentity(path.dirname(candidatePath)),
|
|
886
|
+
captureSkillLinkIdentity(candidatePath),
|
|
887
|
+
])
|
|
888
|
+
).filter((identity): identity is SkillLinkIdentity => identity !== null);
|
|
889
|
+
await SkillDiscoveryTestHooks.afterSkillLinksCaptured?.(candidatePath);
|
|
785
890
|
const skillPath = await fs.promises.realpath(candidatePath);
|
|
786
|
-
|
|
787
|
-
|
|
891
|
+
await SkillDiscoveryTestHooks.afterSkillPathResolved?.(candidatePath);
|
|
892
|
+
const allowOutsideRoot = options.allowExternalUserSkillSymlinks && level === "user";
|
|
893
|
+
if (!isWithinRoot(skillPath) && !allowOutsideRoot) {
|
|
894
|
+
const remedy =
|
|
895
|
+
level === "user"
|
|
896
|
+
? `add ${path.dirname(path.dirname(skillPath))} to skills.customDirectories to load it, or enable skills.trustUserSkills`
|
|
897
|
+
: "keep project skill targets inside the skills scan root";
|
|
898
|
+
warnings.push(
|
|
899
|
+
`Refusing skill path outside scan root: ${candidatePath} (resolves to ${skillPath}; ${remedy})`,
|
|
900
|
+
);
|
|
788
901
|
return;
|
|
789
902
|
}
|
|
790
|
-
const stat = await fs.promises.stat(skillPath);
|
|
791
|
-
if (!stat.isFile() || stat.nlink !==
|
|
903
|
+
const stat = await fs.promises.stat(skillPath, { bigint: true });
|
|
904
|
+
if (!stat.isFile() || stat.nlink !== 1n) {
|
|
792
905
|
warnings.push(`Skill path is not a regular file: ${candidatePath}`);
|
|
793
906
|
return;
|
|
794
907
|
}
|
|
908
|
+
const pinExternalTarget = !isWithinRoot(skillPath) && allowOutsideRoot;
|
|
909
|
+
const fileLink = linkIdentities.find(identity => identity.path === candidatePath);
|
|
910
|
+
const parentLink = linkIdentities.find(identity => identity.path === path.dirname(candidatePath));
|
|
911
|
+
let capturedLinkTargetPath: string | undefined;
|
|
912
|
+
if (fileLink?.target.kind === "file") {
|
|
913
|
+
capturedLinkTargetPath = fileLink.target.realPath;
|
|
914
|
+
} else if (parentLink?.target.kind === "directory") {
|
|
915
|
+
capturedLinkTargetPath = path.join(parentLink.target.realPath, path.basename(candidatePath));
|
|
916
|
+
}
|
|
917
|
+
if (
|
|
918
|
+
pinExternalTarget &&
|
|
919
|
+
(!capturedLinkTargetPath ||
|
|
920
|
+
normalizePathForComparison(skillPath) !== normalizePathForComparison(capturedLinkTargetPath))
|
|
921
|
+
) {
|
|
922
|
+
warnings.push(`Refusing skill path not bound to its captured external link target: ${candidatePath}`);
|
|
923
|
+
return;
|
|
924
|
+
}
|
|
925
|
+
const fileIdentity = pinExternalTarget ? { dev: stat.dev, ino: stat.ino, realPath: skillPath } : undefined;
|
|
926
|
+
const pinnedLinkIdentities = pinExternalTarget ? linkIdentities : [];
|
|
795
927
|
await SkillDiscoveryTestHooks.afterCandidateValidated?.(candidatePath);
|
|
796
|
-
const { frontmatter, size } = await readSkillFrontmatterSafely(skillPath);
|
|
928
|
+
const { frontmatter, size } = await readSkillFrontmatterSafely(skillPath, fileIdentity, pinnedLinkIdentities);
|
|
929
|
+
if (!(await skillPathAndLinksAreCurrent(skillPath, fileIdentity, pinnedLinkIdentities))) {
|
|
930
|
+
throw new Error(`Unsafe skill link for: ${candidatePath}`);
|
|
931
|
+
}
|
|
797
932
|
if (!frontmatter) {
|
|
798
933
|
if (size > SKILL_FRONTMATTER_SCAN_TOTAL_BYTES) {
|
|
799
934
|
warnings.push(
|
|
@@ -817,8 +952,9 @@ export async function scanSkillsFromDir(
|
|
|
817
952
|
items.push({
|
|
818
953
|
name,
|
|
819
954
|
path: skillPath,
|
|
955
|
+
discoveryPath: candidatePath,
|
|
820
956
|
loadContent: async () => {
|
|
821
|
-
const content = await readSkillContentSafely(skillPath);
|
|
957
|
+
const content = await readSkillContentSafely(skillPath, fileIdentity, pinnedLinkIdentities);
|
|
822
958
|
return parseFrontmatter(content, { source: skillPath }).body;
|
|
823
959
|
},
|
|
824
960
|
frontmatter: frontmatter as SkillFrontmatter,
|
|
@@ -917,8 +1053,17 @@ export async function readContainedFile(
|
|
|
917
1053
|
* Expand environment variables in a string.
|
|
918
1054
|
* Supports ${VAR} and ${VAR:-default} syntax.
|
|
919
1055
|
*/
|
|
920
|
-
|
|
1056
|
+
const SENSITIVE_ENV_NAME = /key|secret|token|pass|auth|credential|cookie|dsn|url$/i;
|
|
1057
|
+
|
|
1058
|
+
export function isSensitiveEnvName(name: string): boolean {
|
|
1059
|
+
return SENSITIVE_ENV_NAME.test(name);
|
|
1060
|
+
}
|
|
1061
|
+
|
|
1062
|
+
function expandEnvVars(value: string, extraEnv?: Record<string, string>, skipSensitiveNames = false): string {
|
|
921
1063
|
return value.replace(/\$\{([^}:]+)(?::-([^}]*))?\}/g, (_, varName: string, defaultValue?: string) => {
|
|
1064
|
+
if (skipSensitiveNames && isSensitiveEnvName(varName)) {
|
|
1065
|
+
return defaultValue !== undefined ? defaultValue : `\${${varName}}`;
|
|
1066
|
+
}
|
|
922
1067
|
const envValue = extraEnv?.[varName] ?? Bun.env[varName];
|
|
923
1068
|
if (envValue !== undefined) return envValue;
|
|
924
1069
|
if (defaultValue !== undefined) return defaultValue;
|
|
@@ -929,17 +1074,17 @@ function expandEnvVars(value: string, extraEnv?: Record<string, string>): string
|
|
|
929
1074
|
/**
|
|
930
1075
|
* Recursively expand environment variables in an object.
|
|
931
1076
|
*/
|
|
932
|
-
export function expandEnvVarsDeep<T>(obj: T, extraEnv?: Record<string, string
|
|
1077
|
+
export function expandEnvVarsDeep<T>(obj: T, extraEnv?: Record<string, string>, skipSensitiveNames = false): T {
|
|
933
1078
|
if (typeof obj === "string") {
|
|
934
|
-
return expandEnvVars(obj, extraEnv) as T;
|
|
1079
|
+
return expandEnvVars(obj, extraEnv, skipSensitiveNames) as T;
|
|
935
1080
|
}
|
|
936
1081
|
if (Array.isArray(obj)) {
|
|
937
|
-
return obj.map(item => expandEnvVarsDeep(item, extraEnv)) as T;
|
|
1082
|
+
return obj.map(item => expandEnvVarsDeep(item, extraEnv, skipSensitiveNames)) as T;
|
|
938
1083
|
}
|
|
939
1084
|
if (obj !== null && typeof obj === "object") {
|
|
940
1085
|
const result: Record<string, unknown> = {};
|
|
941
1086
|
for (const [key, value] of Object.entries(obj)) {
|
|
942
|
-
result[key] = expandEnvVarsDeep(value, extraEnv);
|
|
1087
|
+
result[key] = expandEnvVarsDeep(value, extraEnv, skipSensitiveNames);
|
|
943
1088
|
}
|
|
944
1089
|
return result as T;
|
|
945
1090
|
}
|
|
@@ -217,6 +217,12 @@ function transformMCPConfig(config: MCPConfigFile, source: SourceMeta, quiet = f
|
|
|
217
217
|
cwd: serverConfig.cwd,
|
|
218
218
|
url: serverConfig.url,
|
|
219
219
|
headers: serverConfig.headers,
|
|
220
|
+
publicNetwork:
|
|
221
|
+
serverConfig.type === "http" &&
|
|
222
|
+
"publicNetwork" in serverConfig &&
|
|
223
|
+
(serverConfig as { publicNetwork?: boolean }).publicNetwork === true
|
|
224
|
+
? true
|
|
225
|
+
: undefined,
|
|
220
226
|
auth: serverConfig.auth,
|
|
221
227
|
oauth: serverConfig.oauth,
|
|
222
228
|
transport: serverConfig.type,
|
package/src/discovery/ssh.ts
CHANGED
|
@@ -34,9 +34,14 @@ interface SSHConfigFile {
|
|
|
34
34
|
|
|
35
35
|
function parsePort(value: number | string | undefined): number | undefined {
|
|
36
36
|
if (value === undefined) return undefined;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
37
|
+
// The JSON is not validated against SSHConfigFile, so anything else (e.g. an
|
|
38
|
+
// array, which parseInt would coerce: parseInt([2222]) === 2222) is rejected.
|
|
39
|
+
if (typeof value !== "number" && typeof value !== "string") return undefined;
|
|
40
|
+
// `Number.parseInt` accepts trailing garbage (parseInt("22oops") === 22), so a
|
|
41
|
+
// string must be plain digits.
|
|
42
|
+
if (typeof value === "string" && !/^\d+$/.test(value)) return undefined;
|
|
43
|
+
const port = typeof value === "number" ? value : Number.parseInt(value, 10);
|
|
44
|
+
return Number.isInteger(port) && port >= 1 && port <= 65535 ? port : undefined;
|
|
40
45
|
}
|
|
41
46
|
|
|
42
47
|
function parseCompat(value: boolean | string | undefined): boolean | undefined {
|
|
@@ -114,7 +119,7 @@ async function loadSshJsonFile(
|
|
|
114
119
|
warnings.push(`Failed to parse JSON in ${canonicalPath}`);
|
|
115
120
|
return { items, warnings };
|
|
116
121
|
}
|
|
117
|
-
const config = expandEnvVarsDeep(parsed);
|
|
122
|
+
const config = expandEnvVarsDeep(parsed, undefined, level === "project");
|
|
118
123
|
if (!config.hosts || typeof config.hosts !== "object") {
|
|
119
124
|
warnings.push(`Missing hosts in ${canonicalPath}`);
|
|
120
125
|
return { items, warnings };
|
|
@@ -8,7 +8,9 @@ const GJC_VAR = "$" + "{GJC_PLUGIN_ROOT}";
|
|
|
8
8
|
|
|
9
9
|
export function substitutePluginRoot<T>(value: T, rootPath: string): T {
|
|
10
10
|
if (typeof value === "string") {
|
|
11
|
-
|
|
11
|
+
// Function replacers: a string replacement would expand `$&`, `$$`, `` $` ``
|
|
12
|
+
// and `$'` inside the install path instead of inserting it verbatim.
|
|
13
|
+
return value.replaceAll(CLAUDE_VAR, () => rootPath).replaceAll(GJC_VAR, () => rootPath) as T;
|
|
12
14
|
}
|
|
13
15
|
if (Array.isArray(value)) {
|
|
14
16
|
return value.map(v => substitutePluginRoot(v, rootPath)) as T;
|
|
@@ -27,7 +27,7 @@ import type { ToolSession } from "../tools";
|
|
|
27
27
|
|
|
28
28
|
const MAX_PATHS_PER_SESSION = 30;
|
|
29
29
|
/** Snapshot generations retained per path (newest first). */
|
|
30
|
-
const MAX_GENERATIONS_PER_PATH =
|
|
30
|
+
const MAX_GENERATIONS_PER_PATH = 8;
|
|
31
31
|
|
|
32
32
|
export interface FileReadSnapshot {
|
|
33
33
|
/** 1-indexed line number → exact line content as observed by `read`/`search`. */
|