get-tbd 0.1.28 → 0.1.30
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/bin.mjs +444 -126
- package/dist/bin.mjs.map +1 -1
- package/dist/cli.mjs +442 -128
- package/dist/cli.mjs.map +1 -1
- package/dist/{config-C0ITTrtc.mjs → config-BPHcePSm.mjs} +1 -1
- package/dist/{config-B38rbI9u.mjs → config-DVap9omo.mjs} +6 -2
- package/dist/config-DVap9omo.mjs.map +1 -0
- package/dist/docs/SKILL.md +0 -5
- package/dist/docs/guidelines/bun-monorepo-patterns.md +92 -289
- package/dist/docs/guidelines/cli-agent-skill-patterns.md +1023 -1244
- package/dist/docs/guidelines/pnpm-monorepo-patterns.md +104 -321
- package/dist/docs/guidelines/supply-chain-hardening.md +237 -0
- package/dist/docs/install/ensure-gh-cli.sh +59 -24
- package/dist/docs/shortcuts/standard/new-validation-plan.md +1 -1
- package/dist/index.mjs +1 -1
- package/dist/{src-D2xEmH4L.mjs → src-BK_EF6mk.mjs} +2 -2
- package/dist/{src-D2xEmH4L.mjs.map → src-BK_EF6mk.mjs.map} +1 -1
- package/dist/tbd +444 -126
- package/package.json +1 -1
- package/dist/config-B38rbI9u.mjs.map +0 -1
package/dist/cli.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { S as IssueTitle, b as IssueSchema, g as ISSUE_TITLE_MAX_LENGTH, n as AtticEntrySchema, t as ATTIC_ENTRY_FIELD_ORDER, x as IssueStatus, y as IssueKind } from "./schemas-C8mOQykE.mjs";
|
|
2
|
-
import { a as insertAfterFrontmatter, c as noopLogger, i as serializeIssue, n as parseIssue, o as parseMarkdown, r as parseMarkdownWithFrontmatter, s as stripFrontmatter, t as VERSION$1 } from "./src-
|
|
2
|
+
import { a as insertAfterFrontmatter, c as noopLogger, i as serializeIssue, n as parseIssue, o as parseMarkdown, r as parseMarkdownWithFrontmatter, s as stripFrontmatter, t as VERSION$1 } from "./src-BK_EF6mk.mjs";
|
|
3
3
|
import { a as parseYamlWithConflictDetection, d as PAGINATION_LINE_THRESHOLD, f as PARENT_CONTEXT_MAX_LINES, l as comparisonChain, n as detectDuplicateYamlKeys, o as sortKeys, s as stringifyYaml, u as ordering } from "./yaml-utils-BPy991by.mjs";
|
|
4
|
-
import { A as
|
|
4
|
+
import { A as getWorkspaceDir, C as TBD_GUIDELINES_DIR, D as WORKSPACES_DIR, E as TBD_TEMPLATES_DIR, M as resolveAtticDir, N as resolveDataSyncDir, O as WORKTREE_DIR, S as TBD_DOCS_DIR, T as TBD_SHORTCUTS_SYSTEM, _ as DEFAULT_GUIDELINES_PATHS, a as isInitialized, b as SYNC_BRANCH, c as readConfigWithMigration, d as writeConfig, g as DATA_SYNC_DIR_NAME, h as DATA_SYNC_DIR, i as initConfig, j as isValidWorkspaceName, k as WORKTREE_DIR_NAME, l as readLocalState, m as CHARS_PER_TOKEN, n as findTbdRoot, o as markWelcomeSeen, p as CURRENT_FORMAT, r as hasSeenWelcome, s as readConfig, u as updateLocalState, v as DEFAULT_SHORTCUT_PATHS, w as TBD_SHORTCUTS_STANDARD, x as TBD_DIR, y as DEFAULT_TEMPLATE_PATHS } from "./config-DVap9omo.mjs";
|
|
5
5
|
import { _ as formatDisplayId, a as hasShortId, b as normalizeIssueId, c as parseIdMappingFromYaml, d as resolveToInternalId, f as saveIdMapping, g as formatDebugId, h as extractUlidFromInternalId, i as generateUniqueShortId, l as reconcileMappings, m as extractShortId, o as loadIdMapping, p as extractPrefix, s as mergeIdMappings, t as addIdMapping, u as resolveIdMappingConflicts, v as generateInternalId, x as validateIssueId, y as makeInternalId } from "./id-mapping-CqrrLgeX.mjs";
|
|
6
6
|
import { createRequire } from "node:module";
|
|
7
7
|
import { ZodError } from "zod";
|
|
@@ -5845,9 +5845,10 @@ function renderFooter(suggestions, colors) {
|
|
|
5845
5845
|
/**
|
|
5846
5846
|
* Centralized path constants and utilities for coding agent integrations.
|
|
5847
5847
|
*
|
|
5848
|
-
* IMPORTANT: All tbd integration files (hooks, settings,
|
|
5849
|
-
* to PROJECT-LOCAL directories (.
|
|
5850
|
-
* global/user directories
|
|
5848
|
+
* IMPORTANT: All tbd integration files (skills, hooks, settings, scripts) are
|
|
5849
|
+
* installed to PROJECT-LOCAL directories (.agents/, .claude/, .codex/,
|
|
5850
|
+
* scripts/agent/, AGENTS.md) ONLY. We do NOT install to global/user directories
|
|
5851
|
+
* (~/.claude/, ~/.codex/, ~/.agents/).
|
|
5851
5852
|
*
|
|
5852
5853
|
* This file defines all path constants in one place to:
|
|
5853
5854
|
* 1. Ensure consistency across the codebase
|
|
@@ -5855,6 +5856,19 @@ function renderFooter(suggestions, colors) {
|
|
|
5855
5856
|
* 3. Simplify future changes to path conventions
|
|
5856
5857
|
*/
|
|
5857
5858
|
/**
|
|
5859
|
+
* Format version stamped into generated agent integration artifacts (e.g. the
|
|
5860
|
+
* AGENTS.md managed block's begin marker: `... format=f03 surface=...`).
|
|
5861
|
+
*
|
|
5862
|
+
* UNIFIED with the `.tbd/` directory format (`tbd_format`): there is one format
|
|
5863
|
+
* code for all tbd-managed surfaces, sourced from `tbd-format.ts` (the single
|
|
5864
|
+
* source of truth). Bump `CURRENT_FORMAT` there when any managed surface — config
|
|
5865
|
+
* schema OR a generated agent surface — changes shape. A marked AGENTS.md block
|
|
5866
|
+
* with no `format=` field predates this and is treated as `f01`; a running tbd
|
|
5867
|
+
* that finds a HIGHER format than it knows refuses to overwrite it and tells the
|
|
5868
|
+
* user to upgrade tbd.
|
|
5869
|
+
*/
|
|
5870
|
+
const AGENT_INTEGRATION_FORMAT = CURRENT_FORMAT;
|
|
5871
|
+
/**
|
|
5858
5872
|
* Relative path to Claude Code settings file from project root.
|
|
5859
5873
|
* This is where hooks are configured.
|
|
5860
5874
|
*/
|
|
@@ -5888,11 +5902,33 @@ const TBD_CLOSING_REMINDER_REL = ".claude/hooks/tbd-closing-reminder.sh";
|
|
|
5888
5902
|
*/
|
|
5889
5903
|
const GH_CLI_SCRIPT_REL = ".claude/scripts/ensure-gh-cli.sh";
|
|
5890
5904
|
/**
|
|
5905
|
+
* Canonical portable project Agent Skill, scanned by Codex, Gemini CLI, Cursor,
|
|
5906
|
+
* GitHub Copilot, Amp, OpenCode, pi, and other Agent Skills clients.
|
|
5907
|
+
*/
|
|
5908
|
+
const AGENTS_SKILL_REL = ".agents/skills/tbd/SKILL.md";
|
|
5909
|
+
/**
|
|
5910
|
+
* Repository distribution copy of the skill, for skills.sh-style installers
|
|
5911
|
+
* (`npx skills add`) and direct GitHub browsing.
|
|
5912
|
+
*/
|
|
5913
|
+
const SKILLS_DIST_REL = "skills/tbd/SKILL.md";
|
|
5914
|
+
/**
|
|
5891
5915
|
* Relative path to AGENTS.md file from project root.
|
|
5892
5916
|
* Used by Codex, Factory.ai, Cursor (v1.6+), and other compatible tools.
|
|
5893
5917
|
*/
|
|
5894
5918
|
const AGENTS_MD_REL = "AGENTS.md";
|
|
5895
5919
|
/**
|
|
5920
|
+
* Codex project-local config/hook directory.
|
|
5921
|
+
*/
|
|
5922
|
+
const CODEX_DIR_REL = ".codex";
|
|
5923
|
+
/**
|
|
5924
|
+
* Codex project-local hooks file (Claude-compatible event schema).
|
|
5925
|
+
*/
|
|
5926
|
+
const CODEX_HOOKS_REL = ".codex/hooks.json";
|
|
5927
|
+
/**
|
|
5928
|
+
* Codex project-local config; may also carry an inline `[hooks]` table.
|
|
5929
|
+
*/
|
|
5930
|
+
const CODEX_CONFIG_REL = ".codex/config.toml";
|
|
5931
|
+
/**
|
|
5896
5932
|
* Global Claude Code directory in user's home.
|
|
5897
5933
|
* Used ONLY for detecting if Claude Code is installed (for agent detection).
|
|
5898
5934
|
* All installations are project-local.
|
|
@@ -5926,6 +5962,31 @@ function getAgentsMdPath(projectRoot) {
|
|
|
5926
5962
|
return join(projectRoot, AGENTS_MD_REL);
|
|
5927
5963
|
}
|
|
5928
5964
|
/**
|
|
5965
|
+
* Get the three SKILL.md targets: the portable Agent Skills install, the Claude
|
|
5966
|
+
* Code mirror, and the committed distribution copy.
|
|
5967
|
+
*
|
|
5968
|
+
* @param projectRoot - The project root directory (containing .tbd/)
|
|
5969
|
+
*/
|
|
5970
|
+
function getAgentSkillPaths(projectRoot) {
|
|
5971
|
+
return {
|
|
5972
|
+
portable: join(projectRoot, AGENTS_SKILL_REL),
|
|
5973
|
+
claudeMirror: join(projectRoot, CLAUDE_SKILL_REL),
|
|
5974
|
+
distribution: join(projectRoot, SKILLS_DIST_REL)
|
|
5975
|
+
};
|
|
5976
|
+
}
|
|
5977
|
+
/**
|
|
5978
|
+
* Get project-local Codex config/hook paths.
|
|
5979
|
+
*
|
|
5980
|
+
* @param projectRoot - The project root directory
|
|
5981
|
+
*/
|
|
5982
|
+
function getCodexPaths(projectRoot) {
|
|
5983
|
+
return {
|
|
5984
|
+
dir: join(projectRoot, CODEX_DIR_REL),
|
|
5985
|
+
hooks: join(projectRoot, CODEX_HOOKS_REL),
|
|
5986
|
+
config: join(projectRoot, CODEX_CONFIG_REL)
|
|
5987
|
+
};
|
|
5988
|
+
}
|
|
5989
|
+
/**
|
|
5929
5990
|
* Display path for Claude Code settings in status/doctor output.
|
|
5930
5991
|
*/
|
|
5931
5992
|
const CLAUDE_SETTINGS_DISPLAY = "./.claude/settings.json";
|
|
@@ -5933,6 +5994,14 @@ const CLAUDE_SETTINGS_DISPLAY = "./.claude/settings.json";
|
|
|
5933
5994
|
* Display path for AGENTS.md in status/doctor output.
|
|
5934
5995
|
*/
|
|
5935
5996
|
const AGENTS_MD_DISPLAY = "./AGENTS.md";
|
|
5997
|
+
/**
|
|
5998
|
+
* Display path for the portable Agent Skill in status/doctor output.
|
|
5999
|
+
*/
|
|
6000
|
+
const AGENTS_SKILL_DISPLAY = "./.agents/skills/tbd/SKILL.md";
|
|
6001
|
+
/**
|
|
6002
|
+
* Display path for the Codex hooks file in status/doctor output.
|
|
6003
|
+
*/
|
|
6004
|
+
const CODEX_HOOKS_DISPLAY = "./.codex/hooks.json";
|
|
5936
6005
|
|
|
5937
6006
|
//#endregion
|
|
5938
6007
|
//#region src/cli/commands/status.ts
|
|
@@ -5970,10 +6039,14 @@ var StatusHandler = class extends BaseCommand {
|
|
|
5970
6039
|
worktree_healthy: null,
|
|
5971
6040
|
workspaces: [],
|
|
5972
6041
|
integrations: {
|
|
6042
|
+
portable_skill: false,
|
|
6043
|
+
portable_skill_path: AGENTS_SKILL_DISPLAY,
|
|
5973
6044
|
claude_code: false,
|
|
5974
6045
|
claude_code_path: CLAUDE_SETTINGS_DISPLAY,
|
|
5975
6046
|
codex: false,
|
|
5976
|
-
codex_path: AGENTS_MD_DISPLAY
|
|
6047
|
+
codex_path: AGENTS_MD_DISPLAY,
|
|
6048
|
+
codex_hooks: false,
|
|
6049
|
+
codex_hooks_path: CODEX_HOOKS_DISPLAY
|
|
5977
6050
|
}
|
|
5978
6051
|
};
|
|
5979
6052
|
const gitInfo = await this.checkGitRepo();
|
|
@@ -6041,11 +6114,23 @@ var StatusHandler = class extends BaseCommand {
|
|
|
6041
6114
|
const claudePaths = getClaudePaths(projectRoot);
|
|
6042
6115
|
const agentsPath = getAgentsMdPath(projectRoot);
|
|
6043
6116
|
const result = {
|
|
6117
|
+
portable_skill: false,
|
|
6118
|
+
portable_skill_path: AGENTS_SKILL_DISPLAY,
|
|
6044
6119
|
claude_code: false,
|
|
6045
6120
|
claude_code_path: CLAUDE_SETTINGS_DISPLAY,
|
|
6046
6121
|
codex: false,
|
|
6047
|
-
codex_path: AGENTS_MD_DISPLAY
|
|
6122
|
+
codex_path: AGENTS_MD_DISPLAY,
|
|
6123
|
+
codex_hooks: false,
|
|
6124
|
+
codex_hooks_path: CODEX_HOOKS_DISPLAY
|
|
6048
6125
|
};
|
|
6126
|
+
try {
|
|
6127
|
+
await access(getAgentSkillPaths(projectRoot).portable);
|
|
6128
|
+
result.portable_skill = true;
|
|
6129
|
+
} catch {}
|
|
6130
|
+
try {
|
|
6131
|
+
await access(getCodexPaths(projectRoot).hooks);
|
|
6132
|
+
result.codex_hooks = true;
|
|
6133
|
+
} catch {}
|
|
6049
6134
|
try {
|
|
6050
6135
|
await access(claudePaths.settings);
|
|
6051
6136
|
const content = await readFile(claudePaths.settings, "utf-8");
|
|
@@ -6094,15 +6179,28 @@ var StatusHandler = class extends BaseCommand {
|
|
|
6094
6179
|
remote: data.remote,
|
|
6095
6180
|
displayPrefix: data.display_prefix
|
|
6096
6181
|
}, colors);
|
|
6097
|
-
if (renderIntegrationsSection([
|
|
6098
|
-
|
|
6099
|
-
|
|
6100
|
-
|
|
6101
|
-
|
|
6102
|
-
|
|
6103
|
-
|
|
6104
|
-
|
|
6105
|
-
|
|
6182
|
+
if (renderIntegrationsSection([
|
|
6183
|
+
{
|
|
6184
|
+
name: "Portable Agent Skill",
|
|
6185
|
+
installed: data.integrations.portable_skill,
|
|
6186
|
+
path: data.integrations.portable_skill_path
|
|
6187
|
+
},
|
|
6188
|
+
{
|
|
6189
|
+
name: "Claude Code hooks",
|
|
6190
|
+
installed: data.integrations.claude_code,
|
|
6191
|
+
path: data.integrations.claude_code_path
|
|
6192
|
+
},
|
|
6193
|
+
{
|
|
6194
|
+
name: "Codex AGENTS.md",
|
|
6195
|
+
installed: data.integrations.codex,
|
|
6196
|
+
path: data.integrations.codex_path
|
|
6197
|
+
},
|
|
6198
|
+
{
|
|
6199
|
+
name: "Codex hooks",
|
|
6200
|
+
installed: data.integrations.codex_hooks,
|
|
6201
|
+
path: data.integrations.codex_hooks_path
|
|
6202
|
+
}
|
|
6203
|
+
], colors)) {
|
|
6106
6204
|
console.log("");
|
|
6107
6205
|
console.log(`Run ${colors.bold("tbd setup auto")} to configure detected agents`);
|
|
6108
6206
|
}
|
|
@@ -6442,8 +6540,10 @@ var DoctorHandler = class extends BaseCommand {
|
|
|
6442
6540
|
healthChecks.push(await this.checkCloneScenarios());
|
|
6443
6541
|
healthChecks.push(await this.checkSyncConsistency());
|
|
6444
6542
|
const integrationChecks = [];
|
|
6543
|
+
integrationChecks.push(await this.checkPortableSkill());
|
|
6445
6544
|
integrationChecks.push(await this.checkClaudeSkill());
|
|
6446
6545
|
integrationChecks.push(await this.checkCodexAgents());
|
|
6546
|
+
integrationChecks.push(await this.checkCodexHooks());
|
|
6447
6547
|
const allChecks = [...healthChecks, ...integrationChecks];
|
|
6448
6548
|
const allOk = allChecks.every((c) => c.status === "ok");
|
|
6449
6549
|
const hasFixable = allChecks.some((c) => c.fixable && c.status !== "ok");
|
|
@@ -6864,7 +6964,7 @@ var DoctorHandler = class extends BaseCommand {
|
|
|
6864
6964
|
const { parseIdMappingFromYaml, mergeIdMappings } = await import("./id-mapping-Ctfl_nc1.mjs");
|
|
6865
6965
|
let historicalMapping;
|
|
6866
6966
|
try {
|
|
6867
|
-
const syncBranch = (await import("./config-
|
|
6967
|
+
const syncBranch = (await import("./config-BPHcePSm.mjs").then((m) => m.readConfig(this.cwd))).sync.branch;
|
|
6868
6968
|
const logArgs = ["log", "--format=%H"];
|
|
6869
6969
|
if (maxHistory > 0) logArgs.push(`-${maxHistory}`);
|
|
6870
6970
|
logArgs.push(syncBranch, "--", `${DATA_SYNC_DIR}/mappings/ids.yml`);
|
|
@@ -6907,6 +7007,25 @@ var DoctorHandler = class extends BaseCommand {
|
|
|
6907
7007
|
suggestion: "Run: tbd doctor --fix to create missing mappings"
|
|
6908
7008
|
};
|
|
6909
7009
|
}
|
|
7010
|
+
async checkPortableSkill() {
|
|
7011
|
+
const { portable } = getAgentSkillPaths(this.cwd);
|
|
7012
|
+
try {
|
|
7013
|
+
await access(portable);
|
|
7014
|
+
return {
|
|
7015
|
+
name: "Portable Agent Skill",
|
|
7016
|
+
status: "ok",
|
|
7017
|
+
path: AGENTS_SKILL_REL
|
|
7018
|
+
};
|
|
7019
|
+
} catch {
|
|
7020
|
+
return {
|
|
7021
|
+
name: "Portable Agent Skill",
|
|
7022
|
+
status: "warn",
|
|
7023
|
+
message: "not installed",
|
|
7024
|
+
path: AGENTS_SKILL_REL,
|
|
7025
|
+
suggestion: "Run: tbd setup --auto"
|
|
7026
|
+
};
|
|
7027
|
+
}
|
|
7028
|
+
}
|
|
6910
7029
|
async checkClaudeSkill() {
|
|
6911
7030
|
const claudePaths = getClaudePaths(this.cwd);
|
|
6912
7031
|
try {
|
|
@@ -6926,6 +7045,25 @@ var DoctorHandler = class extends BaseCommand {
|
|
|
6926
7045
|
};
|
|
6927
7046
|
}
|
|
6928
7047
|
}
|
|
7048
|
+
async checkCodexHooks() {
|
|
7049
|
+
const codexPaths = getCodexPaths(this.cwd);
|
|
7050
|
+
try {
|
|
7051
|
+
await access(codexPaths.hooks);
|
|
7052
|
+
return {
|
|
7053
|
+
name: "Codex hooks",
|
|
7054
|
+
status: "ok",
|
|
7055
|
+
path: CODEX_HOOKS_REL
|
|
7056
|
+
};
|
|
7057
|
+
} catch {
|
|
7058
|
+
return {
|
|
7059
|
+
name: "Codex hooks",
|
|
7060
|
+
status: "warn",
|
|
7061
|
+
message: "not installed",
|
|
7062
|
+
path: CODEX_HOOKS_REL,
|
|
7063
|
+
suggestion: "Run: tbd setup --auto"
|
|
7064
|
+
};
|
|
7065
|
+
}
|
|
7066
|
+
}
|
|
6929
7067
|
async checkCodexAgents() {
|
|
6930
7068
|
const agentsPath = getAgentsMdPath(this.cwd);
|
|
6931
7069
|
try {
|
|
@@ -8823,7 +8961,7 @@ async function loadSkillContent() {
|
|
|
8823
8961
|
const docsDir = join(dirname(fileURLToPath(import.meta.url)), "..", "..", "..", "docs");
|
|
8824
8962
|
const headerPath = join(docsDir, "install", "claude-header.md");
|
|
8825
8963
|
const skillPath = join(docsDir, "shortcuts", "system", "skill-baseline.md");
|
|
8826
|
-
return await readFile(headerPath, "utf-8") + await readFile(skillPath, "utf-8");
|
|
8964
|
+
return await readFile(headerPath, "utf-8") + stripFrontmatter(await readFile(skillPath, "utf-8"));
|
|
8827
8965
|
} catch {
|
|
8828
8966
|
throw new Error("SKILL.md content file not found. Please rebuild the CLI.");
|
|
8829
8967
|
}
|
|
@@ -9106,7 +9244,7 @@ var SkillHandler = class extends BaseCommand {
|
|
|
9106
9244
|
*/
|
|
9107
9245
|
async composeFullSkill() {
|
|
9108
9246
|
const header = await loadDocContent("install/claude-header.md");
|
|
9109
|
-
const baseSkill = await loadDocContent("shortcuts/system/skill-baseline.md");
|
|
9247
|
+
const baseSkill = stripFrontmatter(await loadDocContent("shortcuts/system/skill-baseline.md"));
|
|
9110
9248
|
const directory = await this.getShortcutDirectory();
|
|
9111
9249
|
let result = header + baseSkill;
|
|
9112
9250
|
if (directory) result = result.trimEnd() + "\n\n" + directory;
|
|
@@ -9863,16 +10001,89 @@ async function getShortcutDirectory(quiet = false) {
|
|
|
9863
10001
|
return generateShortcutDirectory(shortcuts, guidelines);
|
|
9864
10002
|
}
|
|
9865
10003
|
/**
|
|
9866
|
-
*
|
|
9867
|
-
*
|
|
10004
|
+
* DO NOT EDIT marker inserted after the frontmatter of every generated SKILL.md.
|
|
10005
|
+
* Formatted to match flowmark output.
|
|
10006
|
+
*/
|
|
10007
|
+
const SKILL_DO_NOT_EDIT_MARKER = "<!-- DO NOT EDIT: Generated by tbd setup.\nRun 'tbd setup' to update.\n-->";
|
|
10008
|
+
/**
|
|
10009
|
+
* Build the full generated SKILL.md payload: the bundled skill content with the
|
|
10010
|
+
* shortcut/guideline directory appended and a DO NOT EDIT marker after the
|
|
10011
|
+
* frontmatter. This is the single source for every skill surface (the portable
|
|
10012
|
+
* .agents/skills install and the .claude/skills mirror) so they stay identical.
|
|
9868
10013
|
*
|
|
9869
|
-
* @param quiet - If true, suppress auto-sync output
|
|
10014
|
+
* @param quiet - If true, suppress auto-sync output.
|
|
9870
10015
|
*/
|
|
9871
|
-
async function
|
|
9872
|
-
let
|
|
10016
|
+
async function buildSkillPayload(quiet = false) {
|
|
10017
|
+
let skillContent = await loadSkillContent();
|
|
9873
10018
|
const directory = await getShortcutDirectory(quiet);
|
|
9874
|
-
if (directory)
|
|
9875
|
-
|
|
10019
|
+
if (directory) skillContent = skillContent.trimEnd() + "\n\n" + directory;
|
|
10020
|
+
skillContent = insertAfterFrontmatter(skillContent, SKILL_DO_NOT_EDIT_MARKER);
|
|
10021
|
+
return skillContent.trimEnd() + "\n";
|
|
10022
|
+
}
|
|
10023
|
+
/**
|
|
10024
|
+
* Write a generated SKILL.md payload to a target path, creating parent dirs.
|
|
10025
|
+
*/
|
|
10026
|
+
async function writeSkillFile(targetPath, payload) {
|
|
10027
|
+
await mkdir(dirname(targetPath), { recursive: true });
|
|
10028
|
+
await writeFile(targetPath, payload);
|
|
10029
|
+
}
|
|
10030
|
+
/**
|
|
10031
|
+
* AGENTS.md managed-block markers. `CODEX_BEGIN_MARKER` is the stable PREFIX of
|
|
10032
|
+
* the begin line — the metadata (`format=fNN surface=agents-md`) follows it on the
|
|
10033
|
+
* same line, e.g. `<!-- BEGIN TBD INTEGRATION format=f02 surface=agents-md -->`.
|
|
10034
|
+
* Cleanup/upgrade code matches on this prefix so it finds both legacy
|
|
10035
|
+
* (`<!-- BEGIN TBD INTEGRATION -->`) and current marked blocks.
|
|
10036
|
+
*/
|
|
10037
|
+
const CODEX_BEGIN_MARKER = "<!-- BEGIN TBD INTEGRATION";
|
|
10038
|
+
const CODEX_END_MARKER = "<!-- END TBD INTEGRATION -->";
|
|
10039
|
+
/** The full begin marker line for newly generated blocks. */
|
|
10040
|
+
const CODEX_BEGIN_LINE = `${CODEX_BEGIN_MARKER} format=${AGENT_INTEGRATION_FORMAT} surface=agents-md -->`;
|
|
10041
|
+
/** Numeric value of an `fNN` format string, for ordering comparisons. */
|
|
10042
|
+
function formatToNumber(format) {
|
|
10043
|
+
return Number.parseInt(format.replace(/^f/, ""), 10);
|
|
10044
|
+
}
|
|
10045
|
+
/**
|
|
10046
|
+
* Read the integration format (`fNN`) stamped in a generated artifact's begin
|
|
10047
|
+
* marker. Returns `f01` for a legacy marked block with no `format=` field, or
|
|
10048
|
+
* null when there is no tbd-managed block at all.
|
|
10049
|
+
*/
|
|
10050
|
+
function parseIntegrationFormat(content) {
|
|
10051
|
+
const match = /format=f(\d+)/.exec(content);
|
|
10052
|
+
if (match?.[1]) return `f${match[1]}`;
|
|
10053
|
+
return content.includes(CODEX_BEGIN_MARKER) ? "f01" : null;
|
|
10054
|
+
}
|
|
10055
|
+
/**
|
|
10056
|
+
* Forward-compatibility guard. If a generated artifact was written by a NEWER
|
|
10057
|
+
* tbd than this one understands, refuse to rewrite it and tell the user to
|
|
10058
|
+
* upgrade tbd — overwriting would downgrade a newer managed format. This is what
|
|
10059
|
+
* makes pinning safe on a team: an older tbd fails loudly instead of clobbering.
|
|
10060
|
+
*/
|
|
10061
|
+
function assertNotNewerFormat(content, artifact) {
|
|
10062
|
+
const format = parseIntegrationFormat(content);
|
|
10063
|
+
if (format !== null && formatToNumber(format) > formatToNumber(AGENT_INTEGRATION_FORMAT)) throw new CLIError(`${artifact} was generated by a newer tbd (integration format ${format}; this tbd supports up to ${AGENT_INTEGRATION_FORMAT}).\nUpgrade tbd to manage it: npm install -g get-tbd@latest`);
|
|
10064
|
+
}
|
|
10065
|
+
/**
|
|
10066
|
+
* Get the tbd managed block for AGENTS.md.
|
|
10067
|
+
*
|
|
10068
|
+
* This is a COMPACT always-on bootstrap, not a copy of the full skill: it names
|
|
10069
|
+
* tbd, states the operating rule, and points to the CLI commands that provide
|
|
10070
|
+
* progressive disclosure (`tbd prime`, `tbd skill`, `tbd shortcut/guidelines
|
|
10071
|
+
* --list`). The full skill body lives in the SKILL.md surfaces, not here, so the
|
|
10072
|
+
* block stays well under the AGENTS.md prompt-budget guidance.
|
|
10073
|
+
*/
|
|
10074
|
+
function getCodexTbdSection() {
|
|
10075
|
+
return `${CODEX_BEGIN_LINE}\n## tbd
|
|
10076
|
+
|
|
10077
|
+
This repository uses **tbd** for git-native issue tracking (beads), spec-driven
|
|
10078
|
+
planning, and on-demand engineering guidelines.
|
|
10079
|
+
As the agent, you operate tbd on the user’s behalf — translate their requests into tbd
|
|
10080
|
+
actions rather than telling them to run commands.
|
|
10081
|
+
|
|
10082
|
+
- Run \`tbd prime\` to load current project state and the full tbd workflow.
|
|
10083
|
+
- Run \`tbd skill\` for the complete reusable tbd skill instructions.
|
|
10084
|
+
- Run \`tbd shortcut --list\` and \`tbd guidelines --list\` for on-demand resources.
|
|
10085
|
+
- Track all work as beads: \`tbd create\`, \`tbd ready\`, \`tbd close\`, and \`tbd sync\`.
|
|
10086
|
+
\n${CODEX_END_MARKER}\n`;
|
|
9876
10087
|
}
|
|
9877
10088
|
/**
|
|
9878
10089
|
* Script to ensure tbd CLI is installed and run tbd prime.
|
|
@@ -9884,82 +10095,32 @@ async function getCodexTbdSection(quiet = false) {
|
|
|
9884
10095
|
* tbd-session.sh --brief # Ensure tbd + run tbd prime --brief (for PreCompact)
|
|
9885
10096
|
*/
|
|
9886
10097
|
const TBD_SESSION_SCRIPT = `#!/bin/bash
|
|
9887
|
-
# Ensure tbd CLI is
|
|
9888
|
-
# Installed by: tbd setup --auto
|
|
9889
|
-
#
|
|
9890
|
-
|
|
9891
|
-
#
|
|
9892
|
-
|
|
9893
|
-
|
|
9894
|
-
NPM_PREFIX=$(npm config get prefix 2>/dev/null)
|
|
9895
|
-
if [ -n "$NPM_PREFIX" ] && [ -d "$NPM_PREFIX/bin" ]; then
|
|
9896
|
-
NPM_GLOBAL_BIN="$NPM_PREFIX/bin"
|
|
9897
|
-
fi
|
|
9898
|
-
fi
|
|
9899
|
-
|
|
9900
|
-
# Add common binary locations to PATH (persists for entire script)
|
|
9901
|
-
# Include npm global bin if found
|
|
9902
|
-
export PATH="$NPM_GLOBAL_BIN:$HOME/.local/bin:$HOME/bin:/usr/local/bin:$PATH"
|
|
9903
|
-
|
|
9904
|
-
# Function to ensure tbd is available
|
|
9905
|
-
ensure_tbd() {
|
|
9906
|
-
# Check if tbd is already installed
|
|
9907
|
-
if command -v tbd &> /dev/null; then
|
|
9908
|
-
return 0
|
|
9909
|
-
fi
|
|
10098
|
+
# Ensure the tbd CLI is available and run \`tbd prime\`.
|
|
10099
|
+
# Installed by: tbd setup --auto. Runs on SessionStart and PreCompact.
|
|
10100
|
+
#
|
|
10101
|
+
# Local-first, then a VERSION-PINNED zero-install fallback. Pinning is both a
|
|
10102
|
+
# supply-chain control (an unpinned runner re-resolves to latest on every run
|
|
10103
|
+
# and bypasses any cool-off) and a consistency control (every teammate and agent
|
|
10104
|
+
# runs the same tbd version).
|
|
9910
10105
|
|
|
9911
|
-
|
|
10106
|
+
# Prefer common local bin locations.
|
|
10107
|
+
export PATH="$HOME/.local/bin:$HOME/bin:/usr/local/bin:$PATH"
|
|
9912
10108
|
|
|
9913
|
-
|
|
9914
|
-
|
|
9915
|
-
|
|
9916
|
-
|
|
9917
|
-
|
|
9918
|
-
echo "[tbd] Global npm install failed, trying user install..."
|
|
9919
|
-
mkdir -p ~/.local/bin
|
|
9920
|
-
npm install --prefix ~/.local get-tbd
|
|
9921
|
-
# Create symlink if needed
|
|
9922
|
-
if [ -f ~/.local/node_modules/.bin/tbd ]; then
|
|
9923
|
-
ln -sf ~/.local/node_modules/.bin/tbd ~/.local/bin/tbd
|
|
9924
|
-
fi
|
|
9925
|
-
}
|
|
9926
|
-
elif command -v pnpm &> /dev/null; then
|
|
9927
|
-
echo "[tbd] Installing via pnpm..."
|
|
9928
|
-
pnpm add -g get-tbd
|
|
9929
|
-
elif command -v yarn &> /dev/null; then
|
|
9930
|
-
echo "[tbd] Installing via yarn..."
|
|
9931
|
-
yarn global add get-tbd
|
|
9932
|
-
else
|
|
9933
|
-
echo "[tbd] ERROR: No package manager found (npm, pnpm, or yarn required)"
|
|
9934
|
-
echo "[tbd] Please install Node.js and npm, then run: npm install -g get-tbd"
|
|
9935
|
-
return 1
|
|
9936
|
-
fi
|
|
9937
|
-
|
|
9938
|
-
# Verify installation
|
|
9939
|
-
if command -v tbd &> /dev/null; then
|
|
9940
|
-
echo "[tbd] Successfully installed to $(which tbd)"
|
|
9941
|
-
return 0
|
|
9942
|
-
else
|
|
9943
|
-
echo "[tbd] WARNING: tbd installed but not found in PATH"
|
|
9944
|
-
echo "[tbd] Checking common locations..."
|
|
9945
|
-
# Try to find and add to path (include npm global bin)
|
|
9946
|
-
for dir in "$NPM_GLOBAL_BIN" ~/.local/bin ~/.local/node_modules/.bin /usr/local/bin; do
|
|
9947
|
-
if [ -n "$dir" ] && [ -x "$dir/tbd" ]; then
|
|
9948
|
-
export PATH="$dir:$PATH"
|
|
9949
|
-
echo "[tbd] Found at $dir/tbd"
|
|
9950
|
-
return 0
|
|
9951
|
-
fi
|
|
9952
|
-
done
|
|
9953
|
-
echo "[tbd] Could not locate tbd after installation"
|
|
9954
|
-
return 1
|
|
9955
|
-
fi
|
|
9956
|
-
}
|
|
10109
|
+
# Local-first: use tbd if it is already on PATH.
|
|
10110
|
+
if command -v tbd &> /dev/null; then
|
|
10111
|
+
tbd prime "$@"
|
|
10112
|
+
exit $?
|
|
10113
|
+
fi
|
|
9957
10114
|
|
|
9958
|
-
#
|
|
9959
|
-
|
|
10115
|
+
# Pinned zero-install fallback. Never use an unpinned runner here.
|
|
10116
|
+
if command -v npx &> /dev/null; then
|
|
10117
|
+
npx --yes get-tbd@${VERSION} prime "$@"
|
|
10118
|
+
exit $?
|
|
10119
|
+
fi
|
|
9960
10120
|
|
|
9961
|
-
|
|
9962
|
-
tbd
|
|
10121
|
+
echo "[tbd] tbd CLI not found and npx is unavailable."
|
|
10122
|
+
echo "[tbd] Install it with: npm install -g get-tbd@${VERSION}"
|
|
10123
|
+
exit 1
|
|
9963
10124
|
`;
|
|
9964
10125
|
/**
|
|
9965
10126
|
* Claude Code session hooks configuration.
|
|
@@ -10049,22 +10210,14 @@ async function loadBundledScript(name) {
|
|
|
10049
10210
|
throw new Error(`Bundled script not found: ${name}`);
|
|
10050
10211
|
}
|
|
10051
10212
|
/**
|
|
10052
|
-
* AGENTS.md integration markers for Codex/Factory.ai
|
|
10053
|
-
* Content is now generated dynamically from SKILL.md via getCodexTbdSection()
|
|
10054
|
-
*/
|
|
10055
|
-
const CODEX_BEGIN_MARKER = "<!-- BEGIN TBD INTEGRATION -->";
|
|
10056
|
-
const CODEX_END_MARKER = "<!-- END TBD INTEGRATION -->";
|
|
10057
|
-
/**
|
|
10058
10213
|
* Generate a new AGENTS.md file with tbd integration.
|
|
10059
|
-
*
|
|
10060
|
-
* @param quiet - If true, suppress auto-sync output (default: false)
|
|
10061
10214
|
*/
|
|
10062
|
-
|
|
10215
|
+
function getCodexNewAgentsFile() {
|
|
10063
10216
|
return `# Project Instructions for AI Agents
|
|
10064
10217
|
|
|
10065
10218
|
This file provides instructions and context for AI coding agents working on this project.
|
|
10066
10219
|
|
|
10067
|
-
${
|
|
10220
|
+
${getCodexTbdSection()}
|
|
10068
10221
|
## Build & Test
|
|
10069
10222
|
|
|
10070
10223
|
_Add your build and test commands here_
|
|
@@ -10085,6 +10238,54 @@ _Add your project-specific conventions here_
|
|
|
10085
10238
|
`;
|
|
10086
10239
|
}
|
|
10087
10240
|
/**
|
|
10241
|
+
* Codex project-local script paths (relative to repo root). Codex hooks
|
|
10242
|
+
* reference ONLY these `.codex/` paths, never `.claude/`, so Codex setup stays
|
|
10243
|
+
* independent of Claude Code setup.
|
|
10244
|
+
*/
|
|
10245
|
+
const CODEX_SESSION_SCRIPT_REL = ".codex/tbd-session.sh";
|
|
10246
|
+
const CODEX_CLOSING_REMINDER_REL = ".codex/tbd-closing-reminder.sh";
|
|
10247
|
+
const CODEX_GH_CLI_SCRIPT_REL = ".codex/ensure-gh-cli.sh";
|
|
10248
|
+
/**
|
|
10249
|
+
* Build the Codex hooks.json content. Codex uses the same lifecycle event
|
|
10250
|
+
* schema as Claude Code (command handlers), so tbd's hooks map almost 1:1:
|
|
10251
|
+
* SessionStart and PreCompact run `tbd prime`, PostToolUse reminds about sync
|
|
10252
|
+
* after `git push`, and (when enabled) a second SessionStart entry ensures gh.
|
|
10253
|
+
*/
|
|
10254
|
+
function getCodexHooksConfig(useGhCli) {
|
|
10255
|
+
const sessionStart = [{
|
|
10256
|
+
matcher: "",
|
|
10257
|
+
hooks: [{
|
|
10258
|
+
type: "command",
|
|
10259
|
+
command: `bash ${CODEX_SESSION_SCRIPT_REL}`
|
|
10260
|
+
}]
|
|
10261
|
+
}];
|
|
10262
|
+
if (useGhCli) sessionStart.push({
|
|
10263
|
+
matcher: "",
|
|
10264
|
+
hooks: [{
|
|
10265
|
+
type: "command",
|
|
10266
|
+
command: `bash ${CODEX_GH_CLI_SCRIPT_REL}`,
|
|
10267
|
+
timeout: 120
|
|
10268
|
+
}]
|
|
10269
|
+
});
|
|
10270
|
+
return { hooks: {
|
|
10271
|
+
SessionStart: sessionStart,
|
|
10272
|
+
PreCompact: [{
|
|
10273
|
+
matcher: "",
|
|
10274
|
+
hooks: [{
|
|
10275
|
+
type: "command",
|
|
10276
|
+
command: `bash ${CODEX_SESSION_SCRIPT_REL} --brief`
|
|
10277
|
+
}]
|
|
10278
|
+
}],
|
|
10279
|
+
PostToolUse: [{
|
|
10280
|
+
matcher: "Bash",
|
|
10281
|
+
hooks: [{
|
|
10282
|
+
type: "command",
|
|
10283
|
+
command: `bash ${CODEX_CLOSING_REMINDER_REL}`
|
|
10284
|
+
}]
|
|
10285
|
+
}]
|
|
10286
|
+
} };
|
|
10287
|
+
}
|
|
10288
|
+
/**
|
|
10088
10289
|
* Legacy script patterns to clean up from .claude/scripts/
|
|
10089
10290
|
* These were used in older versions of tbd before hooks moved to `tbd prime`
|
|
10090
10291
|
*/
|
|
@@ -10345,13 +10546,7 @@ var SetupClaudeHandler = class extends BaseCommand {
|
|
|
10345
10546
|
await writeFile(claudePaths.closingReminder, TBD_CLOSE_PROTOCOL_SCRIPT);
|
|
10346
10547
|
await chmod(claudePaths.closingReminder, 493);
|
|
10347
10548
|
this.output.success("Installed sync reminder hook script");
|
|
10348
|
-
await
|
|
10349
|
-
let skillContent = await loadSkillContent();
|
|
10350
|
-
const directory = await getShortcutDirectory(this.ctx.quiet);
|
|
10351
|
-
if (directory) skillContent = skillContent.trimEnd() + "\n\n" + directory;
|
|
10352
|
-
skillContent = insertAfterFrontmatter(skillContent, "<!-- DO NOT EDIT: Generated by tbd setup.\nRun 'tbd setup' to update.\n-->");
|
|
10353
|
-
skillContent = skillContent.trimEnd() + "\n";
|
|
10354
|
-
await writeFile(skillPath, skillContent);
|
|
10549
|
+
await writeSkillFile(skillPath, await buildSkillPayload(this.ctx.quiet));
|
|
10355
10550
|
this.output.success("Installed skill file");
|
|
10356
10551
|
this.output.info(` ${skillPath}`);
|
|
10357
10552
|
this.output.info("");
|
|
@@ -10384,16 +10579,93 @@ var SetupCodexHandler = class extends BaseCommand {
|
|
|
10384
10579
|
this.projectDir = dir;
|
|
10385
10580
|
}
|
|
10386
10581
|
async run(options) {
|
|
10387
|
-
const
|
|
10582
|
+
const cwd = this.projectDir ?? process.cwd();
|
|
10583
|
+
const agentsPath = join(cwd, "AGENTS.md");
|
|
10388
10584
|
if (options.check) {
|
|
10389
10585
|
await this.checkCodexSetup(agentsPath);
|
|
10390
10586
|
return;
|
|
10391
10587
|
}
|
|
10392
10588
|
if (options.remove) {
|
|
10393
10589
|
await this.removeCodexSection(agentsPath);
|
|
10590
|
+
await this.removeCodexHooks(cwd);
|
|
10394
10591
|
return;
|
|
10395
10592
|
}
|
|
10396
10593
|
await this.installCodexSection(agentsPath);
|
|
10594
|
+
await this.installCodexHooks(cwd);
|
|
10595
|
+
}
|
|
10596
|
+
/**
|
|
10597
|
+
* Read the use_gh_cli setting; defaults to true (so fresh setup installs it).
|
|
10598
|
+
*/
|
|
10599
|
+
async getUseGhCliSetting(cwd) {
|
|
10600
|
+
try {
|
|
10601
|
+
const tbdRoot = await findTbdRoot(cwd);
|
|
10602
|
+
if (!tbdRoot) return true;
|
|
10603
|
+
return (await readConfig(tbdRoot)).settings.use_gh_cli ?? true;
|
|
10604
|
+
} catch {
|
|
10605
|
+
return true;
|
|
10606
|
+
}
|
|
10607
|
+
}
|
|
10608
|
+
/**
|
|
10609
|
+
* Install Codex lifecycle hooks: writes .codex/ scripts and a .codex/hooks.json
|
|
10610
|
+
* (merged idempotently with any user hooks). Scripts reuse the same bodies as
|
|
10611
|
+
* the Claude install but live under .codex/ so Codex never references .claude/.
|
|
10612
|
+
*/
|
|
10613
|
+
async installCodexHooks(cwd) {
|
|
10614
|
+
if (this.checkDryRun("Would install Codex hooks", { path: "./.codex/hooks.json" })) return;
|
|
10615
|
+
const codexPaths = getCodexPaths(cwd);
|
|
10616
|
+
await mkdir(codexPaths.dir, { recursive: true });
|
|
10617
|
+
const useGhCli = await this.getUseGhCliSetting(cwd);
|
|
10618
|
+
await writeFile(join(cwd, CODEX_SESSION_SCRIPT_REL), TBD_SESSION_SCRIPT);
|
|
10619
|
+
await chmod(join(cwd, CODEX_SESSION_SCRIPT_REL), 493);
|
|
10620
|
+
await writeFile(join(cwd, CODEX_CLOSING_REMINDER_REL), TBD_CLOSE_PROTOCOL_SCRIPT);
|
|
10621
|
+
await chmod(join(cwd, CODEX_CLOSING_REMINDER_REL), 493);
|
|
10622
|
+
if (useGhCli) {
|
|
10623
|
+
const ghScriptContent = await loadBundledScript("ensure-gh-cli.sh");
|
|
10624
|
+
await writeFile(join(cwd, CODEX_GH_CLI_SCRIPT_REL), ghScriptContent);
|
|
10625
|
+
await chmod(join(cwd, CODEX_GH_CLI_SCRIPT_REL), 493);
|
|
10626
|
+
} else await rm(join(cwd, CODEX_GH_CLI_SCRIPT_REL), { force: true });
|
|
10627
|
+
let existing = {};
|
|
10628
|
+
try {
|
|
10629
|
+
existing = JSON.parse(await readFile(codexPaths.hooks, "utf-8"));
|
|
10630
|
+
} catch {}
|
|
10631
|
+
const isTbdOwned = (entry) => {
|
|
10632
|
+
return (entry.hooks ?? []).some((h) => h.command?.includes(".codex/"));
|
|
10633
|
+
};
|
|
10634
|
+
const merged = { ...existing.hooks ?? {} };
|
|
10635
|
+
const tbdHooks = getCodexHooksConfig(useGhCli).hooks;
|
|
10636
|
+
for (const [event, entries] of Object.entries(tbdHooks)) merged[event] = [...(merged[event] ?? []).filter((e) => !isTbdOwned(e)), ...entries];
|
|
10637
|
+
await writeFile(codexPaths.hooks, JSON.stringify({
|
|
10638
|
+
...existing,
|
|
10639
|
+
hooks: merged
|
|
10640
|
+
}, null, 2) + "\n");
|
|
10641
|
+
this.output.success("Installed Codex hooks (.codex/hooks.json)");
|
|
10642
|
+
}
|
|
10643
|
+
/**
|
|
10644
|
+
* Remove tbd-owned Codex hook entries and scripts, preserving user hooks.
|
|
10645
|
+
*/
|
|
10646
|
+
async removeCodexHooks(cwd) {
|
|
10647
|
+
const codexPaths = getCodexPaths(cwd);
|
|
10648
|
+
try {
|
|
10649
|
+
const existing = JSON.parse(await readFile(codexPaths.hooks, "utf-8"));
|
|
10650
|
+
const hooks = existing.hooks ?? {};
|
|
10651
|
+
for (const event of Object.keys(hooks)) {
|
|
10652
|
+
const kept = (hooks[event] ?? []).filter((entry) => {
|
|
10653
|
+
return !(entry.hooks ?? []).some((h) => h.command?.includes(".codex/"));
|
|
10654
|
+
});
|
|
10655
|
+
if (kept.length === 0) delete hooks[event];
|
|
10656
|
+
else hooks[event] = kept;
|
|
10657
|
+
}
|
|
10658
|
+
if (Object.keys(hooks).length === 0) await rm(codexPaths.hooks, { force: true });
|
|
10659
|
+
else await writeFile(codexPaths.hooks, JSON.stringify({
|
|
10660
|
+
...existing,
|
|
10661
|
+
hooks
|
|
10662
|
+
}, null, 2) + "\n");
|
|
10663
|
+
} catch {}
|
|
10664
|
+
for (const rel of [
|
|
10665
|
+
CODEX_SESSION_SCRIPT_REL,
|
|
10666
|
+
CODEX_CLOSING_REMINDER_REL,
|
|
10667
|
+
CODEX_GH_CLI_SCRIPT_REL
|
|
10668
|
+
]) await rm(join(cwd, rel), { force: true });
|
|
10397
10669
|
}
|
|
10398
10670
|
async checkCodexSetup(agentsPath) {
|
|
10399
10671
|
const agentsRelPath = "./AGENTS.md";
|
|
@@ -10478,8 +10750,9 @@ var SetupCodexHandler = class extends BaseCommand {
|
|
|
10478
10750
|
existingContent = await readFile(agentsPath, "utf-8");
|
|
10479
10751
|
} catch {}
|
|
10480
10752
|
let newContent;
|
|
10481
|
-
const tbdSection =
|
|
10753
|
+
const tbdSection = getCodexTbdSection();
|
|
10482
10754
|
if (existingContent) if (existingContent.includes(CODEX_BEGIN_MARKER)) {
|
|
10755
|
+
assertNotNewerFormat(existingContent, "AGENTS.md");
|
|
10483
10756
|
newContent = this.updatetbdSection(existingContent, tbdSection);
|
|
10484
10757
|
await writeFile(agentsPath, newContent);
|
|
10485
10758
|
this.output.success("Updated existing tbd section in AGENTS.md");
|
|
@@ -10489,7 +10762,7 @@ var SetupCodexHandler = class extends BaseCommand {
|
|
|
10489
10762
|
this.output.success("Added tbd section to existing AGENTS.md");
|
|
10490
10763
|
}
|
|
10491
10764
|
else {
|
|
10492
|
-
await writeFile(agentsPath,
|
|
10765
|
+
await writeFile(agentsPath, getCodexNewAgentsFile());
|
|
10493
10766
|
this.output.success("Created new AGENTS.md with tbd integration");
|
|
10494
10767
|
}
|
|
10495
10768
|
this.output.info(` File: ${agentsPath}`);
|
|
@@ -10859,9 +11132,11 @@ var SetupAutoHandler = class extends BaseCommand {
|
|
|
10859
11132
|
console.log(colors.dim(`Cleaned up legacy ${parts.join(" and ")}`));
|
|
10860
11133
|
}
|
|
10861
11134
|
await this.syncDocs(cwd);
|
|
10862
|
-
const
|
|
11135
|
+
const targeting = this.resolveTargeting();
|
|
11136
|
+
await this.installPortableSkill(cwd);
|
|
11137
|
+
const claudeResult = await this.setupClaudeIfDetected(cwd, targeting.claude);
|
|
10863
11138
|
results.push(claudeResult);
|
|
10864
|
-
const codexResult = await this.setupCodexIfDetected(cwd);
|
|
11139
|
+
const codexResult = await this.setupCodexIfDetected(cwd, targeting.codex);
|
|
10865
11140
|
results.push(codexResult);
|
|
10866
11141
|
const installed = results.filter((r) => r.installed && !r.alreadyInstalled);
|
|
10867
11142
|
const alreadyInstalled = results.filter((r) => r.alreadyInstalled);
|
|
@@ -10904,16 +11179,51 @@ var SetupAutoHandler = class extends BaseCommand {
|
|
|
10904
11179
|
if (result.pruned.length > 0) console.log(colors.dim(`Pruned ${result.pruned.length} stale config entry/entries`));
|
|
10905
11180
|
if (result.errors.length > 0) for (const { path, error } of result.errors) console.log(colors.warn(`Warning: ${path}: ${error}`));
|
|
10906
11181
|
}
|
|
10907
|
-
|
|
11182
|
+
/**
|
|
11183
|
+
* Write the canonical portable Agent Skill to .agents/skills/tbd/SKILL.md.
|
|
11184
|
+
* Runs for every initialized repo, independent of agent detection, so the
|
|
11185
|
+
* skill is portable across Codex, Gemini CLI, Cursor, and other clients.
|
|
11186
|
+
*/
|
|
11187
|
+
async installPortableSkill(cwd) {
|
|
11188
|
+
const colors = this.output.getColors();
|
|
11189
|
+
if (this.checkDryRun("Would install portable Agent Skill", { path: AGENTS_SKILL_DISPLAY })) return;
|
|
11190
|
+
const { portable } = getAgentSkillPaths(cwd);
|
|
11191
|
+
await writeSkillFile(portable, await buildSkillPayload(this.ctx.quiet));
|
|
11192
|
+
console.log(` ${colors.success("✓")} Portable Agent Skill (${AGENTS_SKILL_DISPLAY})`);
|
|
11193
|
+
}
|
|
11194
|
+
/**
|
|
11195
|
+
* Resolve which agent surfaces to install from the explicit targeting flags.
|
|
11196
|
+
* `--all`/`--claude`/`--codex` force surfaces on (and suppress auto-detection
|
|
11197
|
+
* of untargeted surfaces); `--skip-claude`/`--skip-codex` force them off; with
|
|
11198
|
+
* no targeting flag each surface falls back to detection-based auto behavior.
|
|
11199
|
+
*/
|
|
11200
|
+
resolveTargeting() {
|
|
11201
|
+
const opts = this.cmd.optsWithGlobals();
|
|
11202
|
+
const all = opts.all === true;
|
|
11203
|
+
const anyPositive = all || opts.claude === true || opts.codex === true;
|
|
11204
|
+
const resolve = (on, skip) => {
|
|
11205
|
+
if (skip === true) return "off";
|
|
11206
|
+
if (on === true || all) return "on";
|
|
11207
|
+
return anyPositive ? "off" : "auto";
|
|
11208
|
+
};
|
|
11209
|
+
return {
|
|
11210
|
+
claude: resolve(opts.claude, opts.skipClaude),
|
|
11211
|
+
codex: resolve(opts.codex, opts.skipCodex)
|
|
11212
|
+
};
|
|
11213
|
+
}
|
|
11214
|
+
async setupClaudeIfDetected(cwd, mode) {
|
|
10908
11215
|
const result = {
|
|
10909
11216
|
name: "Claude Code",
|
|
10910
11217
|
detected: false,
|
|
10911
11218
|
installed: false,
|
|
10912
11219
|
alreadyInstalled: false
|
|
10913
11220
|
};
|
|
10914
|
-
|
|
10915
|
-
|
|
10916
|
-
|
|
11221
|
+
if (mode === "off") return result;
|
|
11222
|
+
if (mode === "auto") {
|
|
11223
|
+
const hasClaudeDir = await pathExists(GLOBAL_CLAUDE_DIR);
|
|
11224
|
+
const hasClaudeEnv = Object.keys(process.env).some((k) => k.startsWith("CLAUDE_"));
|
|
11225
|
+
if (!hasClaudeDir && !hasClaudeEnv) return result;
|
|
11226
|
+
}
|
|
10917
11227
|
result.detected = true;
|
|
10918
11228
|
const claudePaths = getClaudePaths(cwd);
|
|
10919
11229
|
try {
|
|
@@ -10933,17 +11243,20 @@ var SetupAutoHandler = class extends BaseCommand {
|
|
|
10933
11243
|
}
|
|
10934
11244
|
return result;
|
|
10935
11245
|
}
|
|
10936
|
-
async setupCodexIfDetected(cwd) {
|
|
11246
|
+
async setupCodexIfDetected(cwd, mode) {
|
|
10937
11247
|
const result = {
|
|
10938
11248
|
name: "Codex/AGENTS.md",
|
|
10939
11249
|
detected: false,
|
|
10940
11250
|
installed: false,
|
|
10941
11251
|
alreadyInstalled: false
|
|
10942
11252
|
};
|
|
11253
|
+
if (mode === "off") return result;
|
|
10943
11254
|
const agentsPath = getAgentsMdPath(cwd);
|
|
10944
11255
|
const hasAgentsMd = await pathExists(agentsPath);
|
|
10945
|
-
|
|
10946
|
-
|
|
11256
|
+
if (mode === "auto") {
|
|
11257
|
+
const hasCodexEnv = Object.keys(process.env).some((k) => k.startsWith("CODEX_"));
|
|
11258
|
+
if (!hasAgentsMd && !hasCodexEnv) return result;
|
|
11259
|
+
}
|
|
10947
11260
|
result.detected = true;
|
|
10948
11261
|
if (hasAgentsMd) {
|
|
10949
11262
|
if ((await readFile(agentsPath, "utf-8")).includes("BEGIN TBD INTEGRATION")) result.alreadyInstalled = true;
|
|
@@ -10954,12 +11267,13 @@ var SetupAutoHandler = class extends BaseCommand {
|
|
|
10954
11267
|
await handler.run({});
|
|
10955
11268
|
result.installed = true;
|
|
10956
11269
|
} catch (error) {
|
|
11270
|
+
if (error instanceof CLIError) throw error;
|
|
10957
11271
|
result.error = error.message;
|
|
10958
11272
|
}
|
|
10959
11273
|
return result;
|
|
10960
11274
|
}
|
|
10961
11275
|
};
|
|
10962
|
-
const setupCommand = new Command("setup").description("Configure tbd integration with editors and tools").option("--auto", "Non-interactive mode with smart defaults (for agents/scripts)").option("--interactive", "Interactive mode with prompts (for humans)").option("--from-beads", "Migrate from Beads to tbd").option("--prefix <name>", "Project prefix for issue IDs (required for fresh setup)").option("--force", "Allow non-recommended prefix format (not 2-8 alphabetic)").option("--no-gh-cli", "Disable automatic GitHub CLI installation hook").action(async (options, command) => {
|
|
11276
|
+
const setupCommand = new Command("setup").description("Configure tbd integration with editors and tools").option("--auto", "Non-interactive mode with smart defaults (for agents/scripts)").option("--interactive", "Interactive mode with prompts (for humans)").option("--from-beads", "Migrate from Beads to tbd").option("--prefix <name>", "Project prefix for issue IDs (required for fresh setup)").option("--force", "Allow non-recommended prefix format (not 2-8 alphabetic)").option("--no-gh-cli", "Disable automatic GitHub CLI installation hook").option("--all", "Install every supported agent surface (Claude + Codex)").option("--claude", "Install the Claude Code surface (skill mirror + hooks)").option("--codex", "Install the Codex surface (AGENTS.md block + .codex hooks)").option("--skip-claude", "Skip the Claude Code surface even if detected").option("--skip-codex", "Skip the Codex surface even if detected").action(async (options, command) => {
|
|
10963
11277
|
if (options.auto || options.interactive) {
|
|
10964
11278
|
await new SetupDefaultHandler(command).run(options);
|
|
10965
11279
|
return;
|