mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-31 14:06:50 +01:00
v1.91.284.0 feat(absorb-tstack): /modernize skill, per-skill Codex default prompts, gstack-post --attribute, the unattended grant and a CONTRIBUTING porting section (#3497)
* feat(absorb-tstack): /modernize skill, per-skill Codex default prompts, gstack-post --attribute, the unattended grant and a CONTRIBUTING porting section Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix(modernize): read the PR text through gstack-issue-guard; re-measure the context ceiling Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes - gstack-post: with --attribute, report scan findings at the body file's own line numbers and a finding inside the callout as PART: attribute (the coordinates were shifted by the three callout lines) - /modernize: route commit messages through gstack-issue-guard with the rest of the PR text, read isCrossRepository and maintainerCanModify, fetch a fork head from pull/N/head, refuse a PR whose head is the base branch, and list moved files NUL-safely - docs/unattended.md: say that /ship never rebases or force-pushes and an unattended /modernize stops at its verdict - CHANGELOG: seven skills carry a codex_default_prompt, not six Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: /modernize fetches a fork head with a forced refspec and stops when the PR moved A rerun after the author force-pushed left refs/modernize/pr-N stale (the non-forced refspec is rejected with exit 0 through the pipe), and the lease pinned to the current remote head would then have let the rebased stale head overwrite the author's newer commits. The fetch is forced, Steps 2 and 4 stop when the head is not the one observed in Step 1, and the head-is-base guard applies only to same-repository PRs (a fork's main into main is a valid head). Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: /modernize stops on merge commits in the PR range and pushes a fork head to the fork A plain git rebase drops merge commits and git range-diff compares only the non-merge patches, so a change authored in a PR's merge commit disappeared while the proof printed = for every patch and Step 5 force-pushed the loss. Step 4 now counts merges in $MERGE_BASE..$OLD_HEAD and stops when there are any, switches with -C so a rerun after a refused lease does not fail on the existing branch, and asks for a cross-repository diff to be read as untrusted input before the repository's test command runs it. Step 5 pushes a fork head to the fork's URL with the same pinned lease instead of to origin, where the lease could never match. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test: cover the Codex default-prompt render, the openai.yaml ownership proof and the unattended grant Adds the /ship coverage audit's tests: test/host-renders-openai-yaml-proof.test.ts drives bin/gstack-host-renders.sh's weakened proof against a rendered yaml, a hand-written one and a generic-prompt one; test/unattended-grant.test.ts pins the docs/unattended.md grant section and the handoff pointer; test/gen-skill-docs.test.ts adds modernize to the skills whose codex_default_prompt must reach openai.yaml. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: name the static lists a new skill still needs and the Codex metadata file From the ship documentation audit: CONTRIBUTING's new-skill checklist said no static list needs a row, which this PR disproved (lib/doctor-components.ts SKILL_REQUIREMENTS and the catalog rows); the host comparison table and docs/ADDING_A_HOST.md gain the agents/openai.yaml default_prompt source; a blank line before the new docs/unattended.md grant heading. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes CHANGELOG [Unreleased] gate-integrity record: the catalog and context-budget figures re-measured on top of main (1,314 -> 1,359; modernize 9466; alwaysOnTotal 7458 -> 7880), as the merge commit's ratchets state. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs: name the optional codex_default_prompt key in the new-skill checklist Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * test(parity): re-pin the ship union cap at 1.7405 after the main merge GSTA-36 grant 2026-10-27. On the tree merged withf27a7605the ship union is 326,691 / 187,706 = 1.7404 against #3487's 1.7400; the only ship bytes this PR adds are the handoff section's one-line pointer. Cap 1.7400 -> 1.7405, measurement appended to the comment. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * docs(changelog): the [Unreleased] gate-integrity bullet names the ship cap re-pin Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes CHANGELOG [Unreleased]: end the Codex default-prompts sentence at "generator input only." so bin/gstack-prose-lint --unreleased --gate (main's new quality-gate step) passes, and restate the gate-integrity bullet's figures for the tree merged with1f246e35: ship cap 1.7524 -> 1.7528 (329,005 / 187,706), modernize 9466 -> 3772 after the preamble hoist, alwaysOnTotal 7769 -> 7880, catalog 1,359 unchanged. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> * fix: pre-landing review fixes CHANGELOG [Unreleased] gate-integrity bullet names the current ship cap pin (1.7525 -> 1.7529, measured 329,024 / 187,706 on the392f50d4merge) and keeps the 1.7528 pin in the earlier-merges clause. Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com> --------- Co-authored-by: capy-ai[bot] <230910855+capy-ai[bot]@users.noreply.github.com>
This commit is contained in:
1 parent
215c76e334
commit
10b77de4f2
46 files changed
+1217
-37
No files matched your search
@@ -433,11 +433,28 @@ function condenseOpenAIShortDescription(description: string): string {
|
||||
return `${safe}...`;
|
||||
}
|
||||
|
||||
function generateOpenAIYaml(displayName: string, shortDescription: string): string {
|
||||
/**
|
||||
* The sentence Codex's skill picker inserts when a user chooses the skill.
|
||||
* A template sets it with a `codex_default_prompt:` frontmatter key (one line,
|
||||
* optionally quoted; `$<display name>` is Codex's own skill reference). Without
|
||||
* the key the prompt is the generic fallback, so every skill keeps a valid entry.
|
||||
*/
|
||||
export function extractCodexDefaultPrompt(tmplContent: string): string | undefined {
|
||||
const fmEnd = tmplContent.startsWith('---\n') ? tmplContent.indexOf('\n---', 4) : -1;
|
||||
if (fmEnd === -1) return undefined;
|
||||
const match = tmplContent.slice(4, fmEnd).match(/^codex_default_prompt:[ \t]*(.+?)[ \t]*$/m);
|
||||
if (!match) return undefined;
|
||||
const raw = match[1];
|
||||
const quoted = raw.match(/^"(.*)"$/) || raw.match(/^'(.*)'$/);
|
||||
const value = quoted ? quoted[1] : raw;
|
||||
return value.length > 0 ? value : undefined;
|
||||
}
|
||||
|
||||
export function generateOpenAIYaml(displayName: string, shortDescription: string, defaultPrompt?: string): string {
|
||||
return `interface:
|
||||
display_name: ${JSON.stringify(displayName)}
|
||||
short_description: ${JSON.stringify(shortDescription)}
|
||||
default_prompt: ${JSON.stringify(`Use ${displayName} for this task.`)}
|
||||
default_prompt: ${JSON.stringify(defaultPrompt ?? `Use ${displayName} for this task.`)}
|
||||
policy:
|
||||
allow_implicit_invocation: true
|
||||
`;
|
||||
@@ -749,10 +766,11 @@ function processExternalHost(
|
||||
// section cross-references get the same per-host treatment as SKILL.md).
|
||||
result = applyHostRewrites(result, hostConfig);
|
||||
|
||||
// Config-driven: generate metadata (e.g., openai.yaml for Codex)
|
||||
// Config-driven: generate metadata (e.g., openai.yaml for Codex). The
|
||||
// default_prompt comes from the template's codex_default_prompt: key when set.
|
||||
const metadata = hostConfig.generation.generateMetadata && !symlinkLoop ? {
|
||||
outputPath: path.join(outputDir, 'agents', 'openai.yaml'),
|
||||
content: generateOpenAIYaml(name, condenseOpenAIShortDescription(extractedDescription)),
|
||||
content: generateOpenAIYaml(name, condenseOpenAIShortDescription(extractedDescription), extractCodexDefaultPrompt(tmplContent)),
|
||||
} : undefined;
|
||||
|
||||
return { content: result, outputPath, symlinkLoop, metadata };
|
||||
|
||||
@@ -97,7 +97,7 @@ export interface HostConfig {
|
||||
|
||||
// --- Generation ---
|
||||
generation: {
|
||||
/** Whether to create a metadata file alongside skills (always openai.yaml; gen-skill-docs hardcodes the format). */
|
||||
/** Whether to create a metadata file alongside skills (always openai.yaml; gen-skill-docs hardcodes the format and reads the template's `codex_default_prompt:` key for its default_prompt). */
|
||||
generateMetadata: boolean;
|
||||
/** Skill directories to exclude from generation for this host. */
|
||||
skipSkills?: string[];
|
||||
|
||||
Reference in new issue
Block a user