@opengsd/gsd-pi 1.2.0-dev.5457a158 → 1.2.0-dev.84c56d87
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/dist/resources/.managed-resources-content-hash +1 -1
- package/dist/resources/extensions/browser-tools/engine/managed-gsd-browser.js +209 -88
- package/dist/resources/extensions/browser-tools/engine/selection.js +73 -5
- package/dist/resources/extensions/browser-tools/index.js +69 -12
- package/dist/resources/extensions/claude-code-cli/stream-adapter.js +3 -2
- package/dist/resources/extensions/gsd/bootstrap/register-extension.js +19 -0
- package/dist/resources/extensions/gsd/bootstrap/register-hooks.js +27 -9
- package/dist/resources/extensions/gsd/browser-evidence.js +8 -2
- package/dist/resources/extensions/gsd/mcp-filter.js +2 -19
- package/dist/resources/extensions/gsd/uat-policy.js +2 -1
- package/dist/resources/extensions/gsd/unit-registry.js +7 -20
- package/dist/resources/extensions/gsd/web-app-uat.js +45 -8
- package/dist/resources/extensions/search-the-web/native-search.js +5 -3
- package/dist/resources/extensions/shared/browser-contract.js +59 -0
- package/dist/resources/extensions/shared/gsd-browser-cli.js +72 -4
- package/dist/resources/skills/create-skill/references/executable-code.md +1 -1
- package/dist/resources/skills/create-skill/workflows/add-reference.md +8 -3
- package/dist/resources/skills/create-skill/workflows/add-script.md +4 -2
- package/dist/resources/skills/create-skill/workflows/add-template.md +3 -1
- package/dist/resources/skills/create-skill/workflows/add-workflow.md +8 -3
- package/dist/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
- package/dist/resources/skills/create-skill/workflows/verify-skill.md +9 -4
- package/dist/resources/skills/spike-wrap-up/SKILL.md +9 -9
- package/dist/tsconfig.extensions.tsbuildinfo +1 -1
- package/dist/web/standalone/.next/BUILD_ID +1 -1
- package/dist/web/standalone/.next/app-path-routes-manifest.json +9 -9
- package/dist/web/standalone/.next/build-manifest.json +2 -2
- package/dist/web/standalone/.next/prerender-manifest.json +3 -3
- package/dist/web/standalone/.next/server/app/_global-error.html +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_global-error.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_global-error.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.html +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_not-found.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/_not-found.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.html +1 -1
- package/dist/web/standalone/.next/server/app/index.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/__PAGE__.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_full.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_head.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_index.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app/index.segments/_tree.segment.rsc +1 -1
- package/dist/web/standalone/.next/server/app-paths-manifest.json +9 -9
- package/dist/web/standalone/.next/server/chunks/8357.js +1 -1
- package/dist/web/standalone/.next/server/middleware-build-manifest.js +1 -1
- package/dist/web/standalone/.next/server/pages/404.html +1 -1
- package/dist/web/standalone/.next/server/pages/500.html +1 -1
- package/dist/web/standalone/.next/server/server-reference-manifest.json +1 -1
- package/dist/web/standalone/node_modules/node-pty/build/Makefile +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/container.js +18 -26
- package/dist/web/standalone/node_modules/postcss/lib/css-syntax-error.js +14 -47
- package/dist/web/standalone/node_modules/postcss/lib/declaration.js +4 -4
- package/dist/web/standalone/node_modules/postcss/lib/fromJSON.js +3 -3
- package/dist/web/standalone/node_modules/postcss/lib/input.js +29 -54
- package/dist/web/standalone/node_modules/postcss/lib/lazy-result.js +37 -47
- package/dist/web/standalone/node_modules/postcss/lib/map-generator.js +9 -26
- package/dist/web/standalone/node_modules/postcss/lib/no-work-result.js +55 -57
- package/dist/web/standalone/node_modules/postcss/lib/node.js +31 -99
- package/dist/web/standalone/node_modules/postcss/lib/parse.js +1 -1
- package/dist/web/standalone/node_modules/postcss/lib/parser.js +9 -10
- package/dist/web/standalone/node_modules/postcss/lib/postcss.js +12 -12
- package/dist/web/standalone/node_modules/postcss/lib/previous-map.js +11 -30
- package/dist/web/standalone/node_modules/postcss/lib/processor.js +7 -7
- package/dist/web/standalone/node_modules/postcss/lib/result.js +5 -5
- package/dist/web/standalone/node_modules/postcss/lib/rule.js +6 -6
- package/dist/web/standalone/node_modules/postcss/lib/stringifier.js +28 -69
- package/dist/web/standalone/node_modules/postcss/lib/tokenize.js +2 -6
- package/dist/web/standalone/node_modules/postcss/package.json +48 -48
- package/dist/web/standalone/package.json +1 -1
- package/package.json +1 -1
- package/packages/cloud-mcp-gateway/package.json +2 -2
- package/packages/contracts/package.json +1 -1
- package/packages/daemon/package.json +4 -4
- package/packages/gsd-agent-core/package.json +5 -5
- package/packages/gsd-agent-modes/package.json +7 -7
- package/packages/mcp-server/package.json +3 -3
- package/packages/native/package.json +1 -1
- package/packages/pi-agent-core/package.json +1 -1
- package/packages/pi-ai/dist/models.generated.d.ts +66 -178
- package/packages/pi-ai/dist/models.generated.d.ts.map +1 -1
- package/packages/pi-ai/dist/models.generated.js +116 -204
- package/packages/pi-ai/dist/models.generated.js.map +1 -1
- package/packages/pi-ai/package.json +1 -1
- package/packages/pi-coding-agent/package.json +7 -7
- package/packages/pi-tui/dist/tui.d.ts.map +1 -1
- package/packages/pi-tui/dist/tui.js +9 -0
- package/packages/pi-tui/dist/tui.js.map +1 -1
- package/packages/pi-tui/package.json +2 -2
- package/packages/rpc-client/package.json +2 -2
- package/pkg/package.json +1 -1
- package/src/resources/extensions/browser-tools/engine/managed-gsd-browser.ts +265 -98
- package/src/resources/extensions/browser-tools/engine/selection.ts +90 -4
- package/src/resources/extensions/browser-tools/index.ts +71 -13
- package/src/resources/extensions/browser-tools/tests/browser-engine-selection.test.mjs +83 -13
- package/src/resources/extensions/browser-tools/tests/managed-gsd-browser-tools.test.mjs +136 -0
- package/src/resources/extensions/claude-code-cli/stream-adapter.ts +3 -2
- package/src/resources/extensions/gsd/bootstrap/register-extension.ts +24 -0
- package/src/resources/extensions/gsd/bootstrap/register-hooks.ts +48 -4
- package/src/resources/extensions/gsd/browser-evidence.ts +18 -2
- package/src/resources/extensions/gsd/mcp-filter.ts +2 -23
- package/src/resources/extensions/gsd/tests/browser-automation-contract-fixture.ts +39 -0
- package/src/resources/extensions/gsd/tests/browser-contract.test.ts +44 -0
- package/src/resources/extensions/gsd/tests/dispatch-run-uat-browser-tools.test.ts +2 -1
- package/src/resources/extensions/gsd/tests/extension-bootstrap-isolation.test.ts +35 -1
- package/src/resources/extensions/gsd/tests/integration/auto-worktree-milestone-merge.test.ts +7 -11
- package/src/resources/extensions/gsd/tests/integration/auto-worktree.test.ts +20 -58
- package/src/resources/extensions/gsd/tests/integration/gsd-integration-fixture.ts +80 -0
- package/src/resources/extensions/gsd/tests/token-tool-gating.test.ts +35 -0
- package/src/resources/extensions/gsd/tests/uat-policy.test.ts +24 -29
- package/src/resources/extensions/gsd/tests/web-app-uat.test.ts +44 -1
- package/src/resources/extensions/gsd/uat-policy.ts +2 -1
- package/src/resources/extensions/gsd/unit-registry.ts +7 -20
- package/src/resources/extensions/gsd/web-app-uat.ts +51 -8
- package/src/resources/extensions/search-the-web/native-search.ts +5 -3
- package/src/resources/extensions/shared/browser-contract.ts +66 -0
- package/src/resources/extensions/shared/gsd-browser-cli.ts +88 -4
- package/src/resources/skills/create-skill/references/executable-code.md +1 -1
- package/src/resources/skills/create-skill/workflows/add-reference.md +8 -3
- package/src/resources/skills/create-skill/workflows/add-script.md +4 -2
- package/src/resources/skills/create-skill/workflows/add-template.md +3 -1
- package/src/resources/skills/create-skill/workflows/add-workflow.md +8 -3
- package/src/resources/skills/create-skill/workflows/upgrade-to-router.md +10 -5
- package/src/resources/skills/create-skill/workflows/verify-skill.md +9 -4
- package/src/resources/skills/spike-wrap-up/SKILL.md +9 -9
- /package/dist/web/standalone/.next/static/{2p9Rv9pQflAxCBbGVI2vb → AOpDeK_gJHU8OZjRo31gQ}/_buildManifest.js +0 -0
- /package/dist/web/standalone/.next/static/{2p9Rv9pQflAxCBbGVI2vb → AOpDeK_gJHU8OZjRo31gQ}/_ssgManifest.js +0 -0
|
@@ -10,6 +10,7 @@ import { DISCUSS_TOOLS_ALLOWLIST } from "../constants.ts";
|
|
|
10
10
|
import { buildMinimalAutoGsdToolSet, buildMinimalGsdToolSet, buildMinimalGsdWorkflowToolSet, buildRequestScopedGsdToolSet, MINIMAL_AUTO_BASE_TOOL_NAMES, MINIMAL_GSD_TOOL_NAMES, requestHasGsdCustomType, restoreGsdWorkflowTools, scopeGsdWorkflowToolsForDispatch } from "../bootstrap/register-hooks.ts";
|
|
11
11
|
import { filterToolsForProvider } from "../model-router.ts";
|
|
12
12
|
import { applyUnitSkillVisibility } from "../skill-scope.ts";
|
|
13
|
+
import { drainLogs } from "../workflow-logger.ts";
|
|
13
14
|
|
|
14
15
|
test("buildMinimalGsdToolSet preserves non-GSD tools and replaces broad GSD surface", () => {
|
|
15
16
|
const result = buildMinimalGsdToolSet([
|
|
@@ -103,6 +104,40 @@ test("buildMinimalAutoGsdToolSet keeps unit-specific completion tools without al
|
|
|
103
104
|
assert.ok(!result.includes("gsd_complete_slice"));
|
|
104
105
|
});
|
|
105
106
|
|
|
107
|
+
test("buildMinimalAutoGsdToolSet warns when plan-milestone required tools are unresolved", () => {
|
|
108
|
+
drainLogs();
|
|
109
|
+
const result = buildMinimalAutoGsdToolSet(
|
|
110
|
+
[
|
|
111
|
+
"ask_user_questions",
|
|
112
|
+
"bash",
|
|
113
|
+
"read",
|
|
114
|
+
"gsd_milestone_status",
|
|
115
|
+
"gsd_plan_milestone",
|
|
116
|
+
],
|
|
117
|
+
"plan-milestone",
|
|
118
|
+
[
|
|
119
|
+
"ask_user_questions",
|
|
120
|
+
"bash",
|
|
121
|
+
"read",
|
|
122
|
+
"gsd_milestone_status",
|
|
123
|
+
"gsd_plan_milestone",
|
|
124
|
+
],
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
assert.ok(result.includes("gsd_plan_milestone"));
|
|
128
|
+
assert.ok(!result.includes("gsd_plan_slice"));
|
|
129
|
+
|
|
130
|
+
const logs = drainLogs();
|
|
131
|
+
assert.ok(
|
|
132
|
+
logs.some((entry) =>
|
|
133
|
+
entry.component === "bootstrap" &&
|
|
134
|
+
entry.message.includes("buildMinimalAutoGsdToolSet(plan-milestone)") &&
|
|
135
|
+
entry.message.includes("gsd_plan_slice")
|
|
136
|
+
),
|
|
137
|
+
`expected missing gsd_plan_slice bootstrap warning, got ${JSON.stringify(logs)}`,
|
|
138
|
+
);
|
|
139
|
+
});
|
|
140
|
+
|
|
106
141
|
test("buildMinimalAutoGsdToolSet scopes run-uat to UAT-specific and read-only tools", () => {
|
|
107
142
|
const active = ["ask_user_questions", "bash", "read", "edit", "write", "gsd_summary_save"];
|
|
108
143
|
const registered = [
|
|
@@ -5,7 +5,6 @@ import {
|
|
|
5
5
|
classifyUatContent,
|
|
6
6
|
getDeclaredUatType,
|
|
7
7
|
getUatBrowserToolSupportError,
|
|
8
|
-
hasUatBrowserToolSurface,
|
|
9
8
|
isPartialEligibleUatType,
|
|
10
9
|
resolveEffectiveUatType,
|
|
11
10
|
shouldDispatchUatForContent,
|
|
@@ -13,6 +12,12 @@ import {
|
|
|
13
12
|
uatTypeIncludesBrowser,
|
|
14
13
|
validateUatModePolicy,
|
|
15
14
|
} from "../uat-policy.ts";
|
|
15
|
+
import {
|
|
16
|
+
assertBrowserAutomationContractAvailable,
|
|
17
|
+
assertBrowserAutomationContractMissing,
|
|
18
|
+
assertBrowserBackedUatCanDispatch,
|
|
19
|
+
BROWSER_AUTOMATION_CONTRACT_TOOLS,
|
|
20
|
+
} from "./browser-automation-contract-fixture.ts";
|
|
16
21
|
|
|
17
22
|
describe("uat-policy", () => {
|
|
18
23
|
it("defaults missing UAT mode to artifact-driven", () => {
|
|
@@ -63,14 +68,14 @@ describe("uat-policy", () => {
|
|
|
63
68
|
}
|
|
64
69
|
});
|
|
65
70
|
|
|
66
|
-
it("detects
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
71
|
+
it("detects Browser Automation Contract capability across adapters", () => {
|
|
72
|
+
assertBrowserAutomationContractAvailable(BROWSER_AUTOMATION_CONTRACT_TOOLS.piProvider);
|
|
73
|
+
assertBrowserAutomationContractAvailable(BROWSER_AUTOMATION_CONTRACT_TOOLS.externalMcpClient);
|
|
74
|
+
assertBrowserAutomationContractAvailable(BROWSER_AUTOMATION_CONTRACT_TOOLS.externalMcpWildcard);
|
|
75
|
+
assertBrowserAutomationContractAvailable(BROWSER_AUTOMATION_CONTRACT_TOOLS.otherBrowserMcp);
|
|
76
|
+
assertBrowserAutomationContractMissing(BROWSER_AUTOMATION_CONTRACT_TOOLS.workflowOnly);
|
|
77
|
+
assertBrowserAutomationContractMissing(BROWSER_AUTOMATION_CONTRACT_TOOLS.withoutBrowser);
|
|
78
|
+
assertBrowserAutomationContractMissing(undefined);
|
|
74
79
|
});
|
|
75
80
|
|
|
76
81
|
it("reports missing browser tools only for browser-backed UAT with a known tool snapshot", () => {
|
|
@@ -92,26 +97,16 @@ describe("uat-policy", () => {
|
|
|
92
97
|
}),
|
|
93
98
|
null,
|
|
94
99
|
);
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
);
|
|
105
|
-
assert.equal(
|
|
106
|
-
getUatBrowserToolSupportError({
|
|
107
|
-
uatType: "human-experience",
|
|
108
|
-
activeTools: ["read", "gsd_uat_exec"],
|
|
109
|
-
registeredTools: ["mcp__gsd-browser__*"],
|
|
110
|
-
milestoneId: "M001",
|
|
111
|
-
sliceId: "S01",
|
|
112
|
-
}),
|
|
113
|
-
null,
|
|
114
|
-
);
|
|
100
|
+
assertBrowserBackedUatCanDispatch({
|
|
101
|
+
uatType: "human-experience",
|
|
102
|
+
activeTools: BROWSER_AUTOMATION_CONTRACT_TOOLS.withoutBrowser,
|
|
103
|
+
registeredTools: BROWSER_AUTOMATION_CONTRACT_TOOLS.piProvider,
|
|
104
|
+
});
|
|
105
|
+
assertBrowserBackedUatCanDispatch({
|
|
106
|
+
uatType: "human-experience",
|
|
107
|
+
activeTools: BROWSER_AUTOMATION_CONTRACT_TOOLS.withoutBrowser,
|
|
108
|
+
registeredTools: BROWSER_AUTOMATION_CONTRACT_TOOLS.externalMcpWildcard,
|
|
109
|
+
});
|
|
115
110
|
|
|
116
111
|
const error = getUatBrowserToolSupportError({
|
|
117
112
|
uatType: "browser-executable",
|
|
@@ -15,6 +15,10 @@ function scaffoldProject(root: string, pkg: Record<string, unknown>): void {
|
|
|
15
15
|
writeFileSync(join(root, "package.json"), JSON.stringify(pkg, null, 2));
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
+
const LEGACY_ENGINE = { engine: "legacy", source: "probe", reason: "test" } as const;
|
|
19
|
+
const MANAGED_ENGINE = { engine: "gsd-browser", source: "probe", reason: "test" } as const;
|
|
20
|
+
const OFF_ENGINE = { engine: "off", source: "env", reason: "test" } as const;
|
|
21
|
+
|
|
18
22
|
describe("web-app-uat guidance", () => {
|
|
19
23
|
test("returns null for non-web projects", () => {
|
|
20
24
|
const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
|
|
@@ -36,15 +40,54 @@ describe("web-app-uat guidance", () => {
|
|
|
36
40
|
scripts: { dev: "vite" },
|
|
37
41
|
});
|
|
38
42
|
assert.equal(detectWebApp(root), true);
|
|
39
|
-
const block = buildWebAppUatGuidanceBlock(root);
|
|
43
|
+
const block = buildWebAppUatGuidanceBlock(root, LEGACY_ENGINE);
|
|
40
44
|
assert.ok(block);
|
|
41
45
|
assert.match(block!, /browser-executable/);
|
|
46
|
+
assert.match(block!, /Playwright-backed `browser_\*` tools/);
|
|
42
47
|
assert.match(block!, /Playwright scaffolding/);
|
|
43
48
|
} finally {
|
|
44
49
|
rmSync(root, { recursive: true, force: true });
|
|
45
50
|
}
|
|
46
51
|
});
|
|
47
52
|
|
|
53
|
+
test("describes the managed gsd-browser engine when it is the resolved backing", () => {
|
|
54
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
|
|
55
|
+
try {
|
|
56
|
+
scaffoldProject(root, {
|
|
57
|
+
dependencies: { react: "19.0.0" },
|
|
58
|
+
scripts: { dev: "vite" },
|
|
59
|
+
});
|
|
60
|
+
const block = buildWebAppUatGuidanceBlock(root, MANAGED_ENGINE);
|
|
61
|
+
assert.ok(block);
|
|
62
|
+
assert.match(block!, /managed gsd-browser engine/);
|
|
63
|
+
assert.match(block!, /browser-executable/);
|
|
64
|
+
assert.doesNotMatch(block!, /Playwright-backed/);
|
|
65
|
+
} finally {
|
|
66
|
+
rmSync(root, { recursive: true, force: true });
|
|
67
|
+
}
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
test("steers to runtime-executable UAT when browser tools are off", () => {
|
|
71
|
+
const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
|
|
72
|
+
try {
|
|
73
|
+
scaffoldProject(root, {
|
|
74
|
+
dependencies: { react: "19.0.0" },
|
|
75
|
+
scripts: { dev: "vite" },
|
|
76
|
+
});
|
|
77
|
+
const block = buildWebAppUatGuidanceBlock(root, OFF_ENGINE);
|
|
78
|
+
assert.ok(block);
|
|
79
|
+
assert.match(block!, /browser tools are disabled/);
|
|
80
|
+
assert.doesNotMatch(block!, /- `browser-executable`/);
|
|
81
|
+
// mixed/live-runtime require browser tools per UAT_MODE_POLICIES, so the
|
|
82
|
+
// bullet must drop out too — recommending them would dead-end at dispatch.
|
|
83
|
+
assert.doesNotMatch(block!, /- `mixed`/);
|
|
84
|
+
assert.doesNotMatch(block!, /interactive `browser_\*` checks/);
|
|
85
|
+
assert.match(block!, /runtime-executable/);
|
|
86
|
+
} finally {
|
|
87
|
+
rmSync(root, { recursive: true, force: true });
|
|
88
|
+
}
|
|
89
|
+
});
|
|
90
|
+
|
|
48
91
|
test("detects existing Playwright and npm script", () => {
|
|
49
92
|
const root = mkdtempSync(join(tmpdir(), "gsd-web-uat-"));
|
|
50
93
|
try {
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
2
|
// File Purpose: Central UAT mode policy for dispatch, tool presentation, and result validation.
|
|
3
3
|
|
|
4
|
+
import { hasBrowserContractPrefix } from "../shared/browser-contract.js";
|
|
4
5
|
import { extractUatType } from "./files.js";
|
|
5
6
|
import type { UatType } from "./files.js";
|
|
6
7
|
import { hasBrowserRequiredText } from "./browser-evidence.js";
|
|
@@ -126,7 +127,7 @@ export function uatTypeIncludesBrowser(uatType: string | undefined): boolean {
|
|
|
126
127
|
export function isUatBrowserToolName(toolName: string): boolean {
|
|
127
128
|
const parsed = parseMcpToolName(toolName);
|
|
128
129
|
const canonicalName = parsed?.toolName ?? toolName;
|
|
129
|
-
if (canonicalName
|
|
130
|
+
if (hasBrowserContractPrefix(canonicalName)) return true;
|
|
130
131
|
return parsed?.toolName === "*" && parsed.serverName.toLowerCase().includes("browser");
|
|
131
132
|
}
|
|
132
133
|
|
|
@@ -24,6 +24,7 @@
|
|
|
24
24
|
// implicit in auto-prompts.ts builders).
|
|
25
25
|
|
|
26
26
|
import type { CanonicalWorkflowToolName } from "@opengsd/contracts";
|
|
27
|
+
import { BROWSER_CONTRACT_TOOL_NAMES } from "../shared/browser-contract.js";
|
|
27
28
|
import type { GSDModelPhaseKey } from "./preferences-types.js";
|
|
28
29
|
import type { WorkflowMcpAdapterToolName } from "./workflow-tool-surface.js";
|
|
29
30
|
|
|
@@ -82,26 +83,12 @@ export const RUN_UAT_READ_ONLY_TOOL_NAMES = [
|
|
|
82
83
|
"read",
|
|
83
84
|
] as const;
|
|
84
85
|
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
"browser_fill_ref",
|
|
92
|
-
"browser_wait_for",
|
|
93
|
-
"browser_assert",
|
|
94
|
-
"browser_verify",
|
|
95
|
-
"browser_screenshot",
|
|
96
|
-
"browser_snapshot_refs",
|
|
97
|
-
"browser_find",
|
|
98
|
-
"browser_get_console_logs",
|
|
99
|
-
"browser_get_network_logs",
|
|
100
|
-
"browser_evaluate",
|
|
101
|
-
"browser_reload",
|
|
102
|
-
"browser_batch",
|
|
103
|
-
"browser_act",
|
|
104
|
-
] as const;
|
|
86
|
+
/**
|
|
87
|
+
* Browser tools presented to run-uat. A derived view of the Browser Automation
|
|
88
|
+
* Contract vocabulary (shared/browser-contract.ts) — the contract module is the
|
|
89
|
+
* only place browser tool names are declared.
|
|
90
|
+
*/
|
|
91
|
+
export const RUN_UAT_BROWSER_TOOL_NAMES = BROWSER_CONTRACT_TOOL_NAMES;
|
|
105
92
|
|
|
106
93
|
// ─── The registry ─────────────────────────────────────────────────────────
|
|
107
94
|
|
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
// Project/App: gsd-pi
|
|
2
|
-
// File Purpose: Web-app detection and
|
|
2
|
+
// File Purpose: Web-app detection and browser-UAT guidance for planning and slice closeout.
|
|
3
3
|
|
|
4
4
|
import { existsSync, readFileSync } from "node:fs";
|
|
5
5
|
import { resolve } from "node:path";
|
|
6
6
|
|
|
7
|
+
import { resolveAmbientBrowserEngineResolution, type BrowserEngineResolution } from "../browser-tools/engine/selection.js";
|
|
7
8
|
import { detectWebApp } from "../browser-tools/web-app-detect.js";
|
|
9
|
+
import { UAT_MODE_POLICIES, type UatType } from "./uat-policy.js";
|
|
8
10
|
|
|
9
11
|
export { detectWebApp };
|
|
10
12
|
|
|
@@ -47,24 +49,61 @@ export function findPlaywrightTestScript(projectRoot: string): string | null {
|
|
|
47
49
|
return null;
|
|
48
50
|
}
|
|
49
51
|
|
|
52
|
+
function describeBrowserToolBacking(engineResolution: BrowserEngineResolution): string {
|
|
53
|
+
switch (engineResolution.engine) {
|
|
54
|
+
case "gsd-browser":
|
|
55
|
+
return "This project looks browser-facing. GSD exposes `browser_*` tools backed by the managed gsd-browser engine for run-uat.";
|
|
56
|
+
case "legacy":
|
|
57
|
+
return "This project looks browser-facing. GSD exposes Playwright-backed `browser_*` tools for run-uat.";
|
|
58
|
+
case "off":
|
|
59
|
+
return "This project looks browser-facing, but Pi browser tools are disabled (GSD_BROWSER_ENGINE=off) — prefer `runtime-executable` UAT with automated browser test commands.";
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// One bullet per recommended UAT mode; `mode` keys into UAT_MODE_POLICIES so
|
|
64
|
+
// modes that require browser tools drop out of the guidance when the resolved
|
|
65
|
+
// engine provides none (mixed/live-runtime share one bullet and one policy bit).
|
|
66
|
+
const UAT_MODE_GUIDANCE: ReadonlyArray<{ mode: UatType; bullet: string }> = [
|
|
67
|
+
{
|
|
68
|
+
mode: "browser-executable",
|
|
69
|
+
bullet: "- `browser-executable` — navigate to `http://localhost:…`, click, screenshot, assert via `browser_*` tools during run-uat",
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
mode: "runtime-executable",
|
|
73
|
+
bullet: "- `runtime-executable` — run an automated browser test command via `gsd_uat_exec` (for example `npx playwright test`)",
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
mode: "mixed",
|
|
77
|
+
bullet: "- `mixed` / `live-runtime` — combine runtime startup checks with interactive browser verification",
|
|
78
|
+
},
|
|
79
|
+
];
|
|
80
|
+
|
|
50
81
|
/**
|
|
51
82
|
* Markdown block injected into plan/complete-slice prompts when the project
|
|
52
|
-
* looks browser-facing. Returns null for CLI/library-only repos.
|
|
83
|
+
* looks browser-facing. Returns null for CLI/library-only repos. Guidance is
|
|
84
|
+
* composed from the resolved Browser Automation Engine so prompts never claim
|
|
85
|
+
* an engine the runtime is not using; `engineResolution` is injectable for
|
|
86
|
+
* tests and defaults to the ambient resolution.
|
|
53
87
|
*/
|
|
54
|
-
export function buildWebAppUatGuidanceBlock(
|
|
88
|
+
export function buildWebAppUatGuidanceBlock(
|
|
89
|
+
projectRoot: string,
|
|
90
|
+
engineResolution?: BrowserEngineResolution,
|
|
91
|
+
): string | null {
|
|
55
92
|
if (!detectWebApp(projectRoot)) return null;
|
|
56
93
|
|
|
94
|
+
const resolvedEngine = engineResolution ?? resolveAmbientBrowserEngineResolution(projectRoot);
|
|
95
|
+
const browserToolsAvailable = resolvedEngine.engine !== "off";
|
|
57
96
|
const playwrightScript = findPlaywrightTestScript(projectRoot);
|
|
58
97
|
const hasPlaywright = hasPlaywrightTestDependency(projectRoot) || playwrightScript !== null;
|
|
59
98
|
const lines = [
|
|
60
99
|
"### Web App UAT (detected)",
|
|
61
100
|
"",
|
|
62
|
-
|
|
101
|
+
describeBrowserToolBacking(resolvedEngine),
|
|
63
102
|
"",
|
|
64
103
|
"**UAT modes (pick one per slice — do not use `artifact-driven` for browser steps):**",
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
104
|
+
...UAT_MODE_GUIDANCE
|
|
105
|
+
.filter(({ mode }) => browserToolsAvailable || !UAT_MODE_POLICIES[mode].browserTools)
|
|
106
|
+
.map(({ bullet }) => bullet),
|
|
68
107
|
"",
|
|
69
108
|
"**Planning / closeout rules:**",
|
|
70
109
|
"- Preconditions must name the dev-server command and URL (for example `npm run dev` → `http://localhost:3000`)",
|
|
@@ -93,8 +132,12 @@ export function buildWebAppUatGuidanceBlock(projectRoot: string): string | null
|
|
|
93
132
|
"**Playwright scaffolding (first UI slice):** no `playwright` / `@playwright/test` dependency yet.",
|
|
94
133
|
"- Add a planning task that installs Playwright, adds `playwright.config.ts`, and creates a minimal smoke spec (for example `e2e/smoke.spec.ts`)",
|
|
95
134
|
"- Task `verify` should run `npx playwright test` (or the focused spec) with a safe, simple command",
|
|
96
|
-
"- Until specs exist, use `browser-executable` UAT with localhost preconditions and interactive `browser_*` checks at slice closeout",
|
|
97
135
|
);
|
|
136
|
+
if (browserToolsAvailable) {
|
|
137
|
+
lines.push(
|
|
138
|
+
"- Until specs exist, use `browser-executable` UAT with localhost preconditions and interactive `browser_*` checks at slice closeout",
|
|
139
|
+
);
|
|
140
|
+
}
|
|
98
141
|
}
|
|
99
142
|
|
|
100
143
|
return lines.join("\n");
|
|
@@ -243,9 +243,11 @@ export function registerNativeSearchHooks(pi: NativeSearchPI): { getIsAnthropic:
|
|
|
243
243
|
// Anthropic-only tool never leaks into OpenAI Responses requests.
|
|
244
244
|
isAnthropic = isAnthropicProvider && payloadLooksAnthropic !== false;
|
|
245
245
|
} else {
|
|
246
|
-
//
|
|
247
|
-
//
|
|
248
|
-
|
|
246
|
+
// No authoritative provider info available (no event.model, no model_select).
|
|
247
|
+
// Do NOT inject native web_search — guessing on model name alone causes 400
|
|
248
|
+
// "unsupported_value" errors when the actual provider (copilot, openrouter,
|
|
249
|
+
// proxy, etc.) doesn't expose the server-side search tool (#648).
|
|
250
|
+
isAnthropic = false;
|
|
249
251
|
}
|
|
250
252
|
if (!isAnthropic) return;
|
|
251
253
|
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// Project/App: gsd-pi
|
|
2
|
+
// File Purpose: Browser Automation Contract — the single source for the canonical
|
|
3
|
+
// Pi-facing browser tool vocabulary. Engine adapters (legacy Playwright, managed
|
|
4
|
+
// gsd-browser), UAT policy, dispatch preflight, and evidence detection all derive
|
|
5
|
+
// their browser tool knowledge from this module instead of re-listing names.
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Canonical `browser_*` tool names of the Browser Automation Contract.
|
|
9
|
+
*
|
|
10
|
+
* These are the product-level names Units see regardless of which Browser
|
|
11
|
+
* Automation Engine serves them (ADR-024). Adding a capability here is the
|
|
12
|
+
* one-line vocabulary change; the engine adapters and presentation surfaces
|
|
13
|
+
* are typed against this list, so missing coverage fails typecheck.
|
|
14
|
+
*/
|
|
15
|
+
export const BROWSER_CONTRACT_TOOL_NAMES = [
|
|
16
|
+
"browser_navigate",
|
|
17
|
+
"browser_click",
|
|
18
|
+
"browser_type",
|
|
19
|
+
"browser_fill_form",
|
|
20
|
+
"browser_click_ref",
|
|
21
|
+
"browser_fill_ref",
|
|
22
|
+
"browser_wait_for",
|
|
23
|
+
"browser_assert",
|
|
24
|
+
"browser_verify",
|
|
25
|
+
"browser_screenshot",
|
|
26
|
+
"browser_snapshot_refs",
|
|
27
|
+
"browser_find",
|
|
28
|
+
"browser_get_console_logs",
|
|
29
|
+
"browser_get_network_logs",
|
|
30
|
+
"browser_evaluate",
|
|
31
|
+
"browser_reload",
|
|
32
|
+
"browser_batch",
|
|
33
|
+
"browser_act",
|
|
34
|
+
] as const;
|
|
35
|
+
|
|
36
|
+
export type BrowserContractToolName = (typeof BROWSER_CONTRACT_TOOL_NAMES)[number];
|
|
37
|
+
|
|
38
|
+
const BROWSER_CONTRACT_TOOL_NAME_SET: ReadonlySet<string> = new Set(BROWSER_CONTRACT_TOOL_NAMES);
|
|
39
|
+
|
|
40
|
+
export function isBrowserContractToolName(name: string): name is BrowserContractToolName {
|
|
41
|
+
return BROWSER_CONTRACT_TOOL_NAME_SET.has(name);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Whether a canonical (non-MCP-prefixed) tool name belongs to the browser tool
|
|
46
|
+
* family. Broader than the contract list on purpose: an External MCP Client or
|
|
47
|
+
* host integration may supply additional `browser_*` tools that still satisfy
|
|
48
|
+
* browser-backed UAT.
|
|
49
|
+
*/
|
|
50
|
+
export function hasBrowserContractPrefix(canonicalToolName: string): boolean {
|
|
51
|
+
return canonicalToolName.startsWith("browser_");
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Contract tool names whose appearance in prose marks browser-backed UAT
|
|
56
|
+
* activity (requirement or evidence language). Consumed by the
|
|
57
|
+
* browser-evidence regexes so textual detection stays derived from the
|
|
58
|
+
* contract vocabulary.
|
|
59
|
+
*/
|
|
60
|
+
export const BROWSER_EVIDENCE_SIGNAL_TOOL_NAMES = [
|
|
61
|
+
"browser_assert",
|
|
62
|
+
"browser_batch",
|
|
63
|
+
"browser_find",
|
|
64
|
+
"browser_verify",
|
|
65
|
+
"browser_snapshot_refs",
|
|
66
|
+
] as const satisfies readonly BrowserContractToolName[];
|
|
@@ -55,6 +55,14 @@ function parseGsdBrowserVersion(output: string): string | null {
|
|
|
55
55
|
return output.match(/\b(\d+\.\d+\.\d+)\b/)?.[1] ?? null;
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
59
|
+
return !!value && typeof value === "object" && !Array.isArray(value);
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function resolveExplicitGsdBrowserCliPath(env: NodeJS.ProcessEnv): string | undefined {
|
|
63
|
+
return env.GSD_BROWSER_CLI_PATH?.trim() || env.GSD_BROWSER_BIN_PATH?.trim() || undefined;
|
|
64
|
+
}
|
|
65
|
+
|
|
58
66
|
function resolveBundledGsdBrowserPackageVersion(): string | null {
|
|
59
67
|
try {
|
|
60
68
|
const requireFromHere = createRequire(import.meta.url);
|
|
@@ -66,20 +74,28 @@ function resolveBundledGsdBrowserPackageVersion(): string | null {
|
|
|
66
74
|
}
|
|
67
75
|
}
|
|
68
76
|
|
|
77
|
+
// The `gsd-browser --version` subprocess result cannot change mid-session (the
|
|
78
|
+
// engine-switch guard forbids restarting with a different engine), and both the
|
|
79
|
+
// availability probe and the launch-config resolution ask for it at session
|
|
80
|
+
// start — memoize so the up-to-2s spawn happens once per process.
|
|
81
|
+
let cachedPathProbeVersion: string | null | undefined;
|
|
82
|
+
|
|
69
83
|
function resolvePathGsdBrowserVersion(env: NodeJS.ProcessEnv): string | null {
|
|
70
84
|
const explicit = env.GSD_BROWSER_PATH_VERSION?.trim();
|
|
71
85
|
if (explicit) return parseGsdBrowserVersion(explicit);
|
|
86
|
+
if (cachedPathProbeVersion !== undefined) return cachedPathProbeVersion;
|
|
72
87
|
|
|
73
88
|
try {
|
|
74
|
-
|
|
89
|
+
cachedPathProbeVersion = parseGsdBrowserVersion(execFileSync("gsd-browser", ["--version"], {
|
|
75
90
|
encoding: "utf-8",
|
|
76
91
|
env,
|
|
77
92
|
stdio: ["ignore", "pipe", "ignore"],
|
|
78
93
|
timeout: 2000,
|
|
79
94
|
}));
|
|
80
95
|
} catch {
|
|
81
|
-
|
|
96
|
+
cachedPathProbeVersion = null;
|
|
82
97
|
}
|
|
98
|
+
return cachedPathProbeVersion;
|
|
83
99
|
}
|
|
84
100
|
|
|
85
101
|
function shouldPreferPathGsdBrowser(env: NodeJS.ProcessEnv): boolean {
|
|
@@ -91,7 +107,7 @@ function shouldPreferPathGsdBrowser(env: NodeJS.ProcessEnv): boolean {
|
|
|
91
107
|
}
|
|
92
108
|
|
|
93
109
|
export function resolveBundledGsdBrowserCliPath(env: NodeJS.ProcessEnv = process.env): string | null {
|
|
94
|
-
const explicit =
|
|
110
|
+
const explicit = resolveExplicitGsdBrowserCliPath(env);
|
|
95
111
|
if (explicit) return explicit;
|
|
96
112
|
|
|
97
113
|
try {
|
|
@@ -115,6 +131,45 @@ export function resolveBundledGsdBrowserCliPath(env: NodeJS.ProcessEnv = process
|
|
|
115
131
|
return null;
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
export type GsdBrowserCliAvailability =
|
|
135
|
+
| { available: true; via: "explicit-env" | "bundled" | "path"; detail: string }
|
|
136
|
+
| { available: false; detail: string };
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Cheap availability probe for the gsd-browser CLI: explicit env overrides,
|
|
140
|
+
* then the bundled @opengsd/gsd-browser binary (filesystem checks only), then
|
|
141
|
+
* a PATH lookup (one short subprocess, memoized). Used by Browser Automation
|
|
142
|
+
* Engine resolution to decide whether the managed engine is provable before
|
|
143
|
+
* preferring it over legacy Playwright. `via` names the provable source, not
|
|
144
|
+
* necessarily the launch source — resolveGsdBrowserMcpLaunchConfig may still
|
|
145
|
+
* prefer a newer PATH CLI over the bundled one.
|
|
146
|
+
*/
|
|
147
|
+
export function resolveGsdBrowserCliAvailability(env: NodeJS.ProcessEnv = process.env): GsdBrowserCliAvailability {
|
|
148
|
+
const explicitCommand = env.GSD_BROWSER_MCP_COMMAND?.trim();
|
|
149
|
+
if (explicitCommand) {
|
|
150
|
+
return { available: true, via: "explicit-env", detail: `GSD_BROWSER_MCP_COMMAND=${explicitCommand}` };
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
const explicitCliPath = resolveExplicitGsdBrowserCliPath(env);
|
|
154
|
+
if (explicitCliPath) {
|
|
155
|
+
return existsSync(explicitCliPath)
|
|
156
|
+
? { available: true, via: "explicit-env", detail: `CLI at ${explicitCliPath}` }
|
|
157
|
+
: { available: false, detail: `configured gsd-browser CLI path does not exist: ${explicitCliPath}` };
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
const bundledCliPath = resolveBundledGsdBrowserCliPath(env);
|
|
161
|
+
if (bundledCliPath) {
|
|
162
|
+
return { available: true, via: "bundled", detail: `bundled CLI at ${bundledCliPath}` };
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const pathVersion = resolvePathGsdBrowserVersion(env);
|
|
166
|
+
if (pathVersion) {
|
|
167
|
+
return { available: true, via: "path", detail: `gsd-browser ${pathVersion} on PATH` };
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
return { available: false, detail: "no bundled or PATH gsd-browser CLI found" };
|
|
171
|
+
}
|
|
172
|
+
|
|
118
173
|
export function buildGsdBrowserSessionName(projectRoot: string, suffix?: string): string {
|
|
119
174
|
const resolvedProjectRoot = resolve(projectRoot);
|
|
120
175
|
const base = sanitizeSessionSegment(basename(resolvedProjectRoot)) || "project";
|
|
@@ -123,6 +178,35 @@ export function buildGsdBrowserSessionName(projectRoot: string, suffix?: string)
|
|
|
123
178
|
return cleanSuffix ? `gsd-${base}-${hash}-${cleanSuffix}` : `gsd-${base}-${hash}`;
|
|
124
179
|
}
|
|
125
180
|
|
|
181
|
+
/**
|
|
182
|
+
* Recognize an MCP server config (from .mcp.json / Claude settings) as a
|
|
183
|
+
* gsd-browser server. Paired with resolveGsdBrowserMcpLaunchConfig: this module
|
|
184
|
+
* writes the config shape, so it also owns recognizing it. New launch shapes
|
|
185
|
+
* are taught here, in one place.
|
|
186
|
+
*/
|
|
187
|
+
export function isGsdBrowserMcpServerConfig(config: unknown): boolean {
|
|
188
|
+
if (!isRecord(config)) return false;
|
|
189
|
+
|
|
190
|
+
const command = typeof config.command === "string" ? config.command : "";
|
|
191
|
+
if (command.includes("gsd-browser") || command.includes("@opengsd/gsd-browser")) {
|
|
192
|
+
return true;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (isRecord(config.env)) {
|
|
196
|
+
const env = config.env;
|
|
197
|
+
if (
|
|
198
|
+
typeof env.GSD_BROWSER_CLI_PATH === "string"
|
|
199
|
+
|| typeof env.GSD_BROWSER_BIN_PATH === "string"
|
|
200
|
+
|| typeof env.GSD_BROWSER_MCP_COMMAND === "string"
|
|
201
|
+
) {
|
|
202
|
+
return true;
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const args = Array.isArray(config.args) ? config.args.filter((arg): arg is string => typeof arg === "string") : [];
|
|
207
|
+
return args.some((arg) => arg.includes("gsd-browser") || arg.includes("@opengsd/gsd-browser"));
|
|
208
|
+
}
|
|
209
|
+
|
|
126
210
|
export function resolveGsdBrowserMcpLaunchConfig(
|
|
127
211
|
projectRoot: string,
|
|
128
212
|
env: NodeJS.ProcessEnv = process.env,
|
|
@@ -133,7 +217,7 @@ export function resolveGsdBrowserMcpLaunchConfig(
|
|
|
133
217
|
const explicitArgs = parseJsonEnv<unknown>(env, "GSD_BROWSER_MCP_ARGS");
|
|
134
218
|
const explicitEnv = parseJsonEnv<Record<string, string>>(env, "GSD_BROWSER_MCP_ENV");
|
|
135
219
|
const explicitCommand = env.GSD_BROWSER_MCP_COMMAND?.trim();
|
|
136
|
-
const explicitCliPath =
|
|
220
|
+
const explicitCliPath = resolveExplicitGsdBrowserCliPath(env);
|
|
137
221
|
const preferPathCli = !explicitCommand && !explicitCliPath && shouldPreferPathGsdBrowser(env);
|
|
138
222
|
const bundledCliPath = !explicitCommand && !explicitCliPath && !preferPathCli
|
|
139
223
|
? resolveBundledGsdBrowserCliPath(env)
|
|
@@ -45,7 +45,7 @@ skill-name/
|
|
|
45
45
|
**Reference pattern**: In SKILL.md, reference scripts using the `scripts/` path:
|
|
46
46
|
|
|
47
47
|
```bash
|
|
48
|
-
python ~/.
|
|
48
|
+
python ~/.agents/skills/skill-name/scripts/analyze.py input.har
|
|
49
49
|
```
|
|
50
50
|
</scripts_directory>
|
|
51
51
|
</file_organization>
|
|
@@ -10,16 +10,21 @@
|
|
|
10
10
|
## Step 1: Select the Skill
|
|
11
11
|
|
|
12
12
|
```bash
|
|
13
|
-
|
|
13
|
+
# User-global skills
|
|
14
|
+
ls ~/.agents/skills/ 2>/dev/null
|
|
15
|
+
# Project-local skills
|
|
16
|
+
ls .agents/skills/ 2>/dev/null
|
|
14
17
|
```
|
|
15
18
|
|
|
16
19
|
Present numbered list, ask: "Which skill needs a new reference?"
|
|
17
20
|
|
|
21
|
+
Determine `{skill-path}`: use `.agents/skills/{skill-name}` (project-local) if found there, otherwise `~/.agents/skills/{skill-name}` (user-global). Project-local takes precedence because the skill catalog loads it first on name collision.
|
|
22
|
+
|
|
18
23
|
## Step 2: Analyze Current Structure
|
|
19
24
|
|
|
20
25
|
```bash
|
|
21
|
-
cat
|
|
22
|
-
ls
|
|
26
|
+
cat {skill-path}/SKILL.md
|
|
27
|
+
ls {skill-path}/references/ 2>/dev/null
|
|
23
28
|
```
|
|
24
29
|
|
|
25
30
|
Determine:
|
|
@@ -12,6 +12,8 @@ Ask (if not already provided):
|
|
|
12
12
|
- Which skill needs a script?
|
|
13
13
|
- What operation should the script perform?
|
|
14
14
|
|
|
15
|
+
Determine `{skill-path}`: use `.agents/skills/{skill-name}` (project-local) if found there, otherwise `~/.agents/skills/{skill-name}` (user-global). Project-local takes precedence because the skill catalog loads it first on name collision.
|
|
16
|
+
|
|
15
17
|
## Step 2: Analyze Script Need
|
|
16
18
|
|
|
17
19
|
Confirm this is a good script candidate:
|
|
@@ -24,7 +26,7 @@ If not a good fit, suggest alternatives (inline code in workflow, reference exam
|
|
|
24
26
|
## Step 3: Create Scripts Directory
|
|
25
27
|
|
|
26
28
|
```bash
|
|
27
|
-
mkdir -p
|
|
29
|
+
mkdir -p {skill-path}/scripts
|
|
28
30
|
```
|
|
29
31
|
|
|
30
32
|
## Step 4: Design Script
|
|
@@ -58,7 +60,7 @@ set -euo pipefail
|
|
|
58
60
|
## Step 6: Make Executable (if bash)
|
|
59
61
|
|
|
60
62
|
```bash
|
|
61
|
-
chmod +x
|
|
63
|
+
chmod +x {skill-path}/scripts/{script-name}.sh
|
|
62
64
|
```
|
|
63
65
|
|
|
64
66
|
## Step 7: Update Workflow to Use Script
|
|
@@ -12,6 +12,8 @@ Ask (if not already provided):
|
|
|
12
12
|
- Which skill needs a template?
|
|
13
13
|
- What output does this template structure?
|
|
14
14
|
|
|
15
|
+
Determine `{skill-path}`: use `.agents/skills/{skill-name}` (project-local) if found there, otherwise `~/.agents/skills/{skill-name}` (user-global). Project-local takes precedence because the skill catalog loads it first on name collision.
|
|
16
|
+
|
|
15
17
|
## Step 2: Analyze Template Need
|
|
16
18
|
|
|
17
19
|
Confirm this is a good template candidate:
|
|
@@ -24,7 +26,7 @@ If not a good fit, suggest alternatives (workflow guidance, reference examples).
|
|
|
24
26
|
## Step 3: Create Templates Directory
|
|
25
27
|
|
|
26
28
|
```bash
|
|
27
|
-
mkdir -p
|
|
29
|
+
mkdir -p {skill-path}/templates
|
|
28
30
|
```
|
|
29
31
|
|
|
30
32
|
## Step 4: Design Template Structure
|