@geminix/gxpm 0.1.0

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.
Files changed (299) hide show
  1. package/AGENTS.md +148 -0
  2. package/CANON.md +53 -0
  3. package/CLAUDE.md +60 -0
  4. package/CONTEXT.md +49 -0
  5. package/DEBUG.md +59 -0
  6. package/ISSUE_CONTEXT.md +25 -0
  7. package/README.md +143 -0
  8. package/VERSION +1 -0
  9. package/agents/cleanup-auditor/cleanup-auditor.md +56 -0
  10. package/agents/grill-master.md +26 -0
  11. package/agents/implementer.md +32 -0
  12. package/agents/review-army/accessibility-reviewer.md +54 -0
  13. package/agents/review-army/code-quality-reviewer.md +54 -0
  14. package/agents/review-army/security-reviewer.md +56 -0
  15. package/agents/review-army/spec-compliance-reviewer.md +51 -0
  16. package/agents/review-army/test-reviewer.md +55 -0
  17. package/agents/reviewer.md +59 -0
  18. package/agents/ship-audit-army/docs-auditor.md +53 -0
  19. package/agents/ship-audit-army/performance-auditor.md +52 -0
  20. package/agents/ship-audit-army/security-auditor.md +52 -0
  21. package/agents/specifier.md +55 -0
  22. package/agents/triage-officer.md +27 -0
  23. package/bin/gxpm +17 -0
  24. package/bin/gxpm-browser +17 -0
  25. package/bin/gxpm-config +15 -0
  26. package/bin/gxpm-eval +13 -0
  27. package/bin/gxpm-global-discover +15 -0
  28. package/bin/gxpm-init +38 -0
  29. package/bin/gxpm-investigate +194 -0
  30. package/bin/gxpm-uninstall +15 -0
  31. package/bin/gxpm-update-check +165 -0
  32. package/commands/build.md +40 -0
  33. package/commands/help.md +53 -0
  34. package/commands/plan.md +34 -0
  35. package/commands/refine.md +46 -0
  36. package/commands/review.md +34 -0
  37. package/commands/ship.md +37 -0
  38. package/core/ac-check.ts +20 -0
  39. package/core/agent-runtime.ts +363 -0
  40. package/core/artifact-validator.ts +151 -0
  41. package/core/artifacts.ts +313 -0
  42. package/core/autopilot.ts +250 -0
  43. package/core/capabilities.ts +779 -0
  44. package/core/checkpoint.ts +370 -0
  45. package/core/cleanup.ts +32 -0
  46. package/core/command-probe.ts +82 -0
  47. package/core/config.ts +533 -0
  48. package/core/contracts/behavior-spec.schema.ts +38 -0
  49. package/core/contracts/converter.ts +61 -0
  50. package/core/contracts/host.ts +43 -0
  51. package/core/converters/converter.ts +93 -0
  52. package/core/converters/index.ts +8 -0
  53. package/core/converters/managed-artifact.ts +119 -0
  54. package/core/converters/parser.ts +159 -0
  55. package/core/converters/template-renderer.ts +35 -0
  56. package/core/converters/writer.ts +61 -0
  57. package/core/dag-executor.ts +426 -0
  58. package/core/dag-loader.ts +292 -0
  59. package/core/dag-schemas.ts +150 -0
  60. package/core/dispatch.ts +125 -0
  61. package/core/evidence.ts +148 -0
  62. package/core/gate.ts +269 -0
  63. package/core/hook-engine.ts +566 -0
  64. package/core/host-probe.ts +64 -0
  65. package/core/implement.ts +16 -0
  66. package/core/isolation-errors.ts +174 -0
  67. package/core/isolation-resolver.ts +921 -0
  68. package/core/issue-context.ts +381 -0
  69. package/core/issue-readiness.ts +457 -0
  70. package/core/issue-sync.ts +427 -0
  71. package/core/issues.ts +132 -0
  72. package/core/land.ts +108 -0
  73. package/core/orchestrator.ts +54 -0
  74. package/core/phase-artifact.ts +32 -0
  75. package/core/phase-gates.ts +130 -0
  76. package/core/phase-rewind.ts +94 -0
  77. package/core/plan-lint.ts +61 -0
  78. package/core/plan.ts +77 -0
  79. package/core/port-allocation.ts +50 -0
  80. package/core/pr-check.ts +15 -0
  81. package/core/preset-system/preset-resolver.ts +221 -0
  82. package/core/project-init-status.ts +127 -0
  83. package/core/qa.ts +15 -0
  84. package/core/resilience.ts +165 -0
  85. package/core/runs.ts +288 -0
  86. package/core/safe-path.test.ts +80 -0
  87. package/core/safe-path.ts +60 -0
  88. package/core/sdd-gate.test.ts +98 -0
  89. package/core/sdd-gate.ts +134 -0
  90. package/core/self-review.ts +62 -0
  91. package/core/session.ts +70 -0
  92. package/core/ship.ts +86 -0
  93. package/core/specify.ts +173 -0
  94. package/core/state.ts +1002 -0
  95. package/core/template-engine.ts +152 -0
  96. package/core/template-resolver.test.ts +70 -0
  97. package/core/template-resolver.ts +156 -0
  98. package/core/triage.ts +26 -0
  99. package/core/verify.ts +15 -0
  100. package/core/wiki-native.ts +2423 -0
  101. package/core/wiki.ts +27 -0
  102. package/core/workflow-event-emitter.ts +163 -0
  103. package/core/workflows/engine.ts +273 -0
  104. package/core/workflows/expressions.ts +76 -0
  105. package/core/workflows/index.ts +38 -0
  106. package/core/workflows/steps/command.ts +43 -0
  107. package/core/workflows/steps/gate.ts +47 -0
  108. package/core/workflows/steps/gxpm.ts +44 -0
  109. package/core/workflows/steps/linear.ts +31 -0
  110. package/core/workflows/steps/shell.ts +65 -0
  111. package/core/workflows/types.ts +62 -0
  112. package/core/workspace-runtime.ts +227 -0
  113. package/core/worktree-init-steps.ts +647 -0
  114. package/core/worktree-init.ts +330 -0
  115. package/core/worktree-owner.ts +143 -0
  116. package/docs/GXPM_VERIFY.md +98 -0
  117. package/docs/INSTALL_FOR_AGENTS.md +113 -0
  118. package/docs/README.md +57 -0
  119. package/docs/adr/adr-005-multi-platform-skill-converter.md +72 -0
  120. package/docs/agents/domain.md +30 -0
  121. package/docs/agents/issue-tracker.md +30 -0
  122. package/docs/agents/triage-labels.md +32 -0
  123. package/docs/architecture/gxpm-architecture-diagram.md +265 -0
  124. package/docs/architecture/gxpm-current-architecture.md +175 -0
  125. package/docs/architecture/gxpm-current-flow.md +278 -0
  126. package/docs/architecture/gxpm-replacement-architecture.md +211 -0
  127. package/docs/architecture/gxpm-target-architecture.md +449 -0
  128. package/docs/architecture/gxpm-v0-contract.md +311 -0
  129. package/docs/architecture/layered-workflow-boundaries.md +193 -0
  130. package/docs/architecture/preset-system.md +126 -0
  131. package/docs/architecture/scaffold-northstar.md +23 -0
  132. package/docs/brainstorms/2026-05-14-bdd-then-tdd-design.md +320 -0
  133. package/docs/brainstorms/README.md +22 -0
  134. package/docs/brainstorms/docs-knowledge-system-requirements.md +29 -0
  135. package/docs/governance/beta-skill-promotion.md +39 -0
  136. package/docs/governance/development-contract.md +144 -0
  137. package/docs/governance/gherkin-style.md +90 -0
  138. package/docs/governance/host-adapter.md +56 -0
  139. package/docs/governance/skill-authoring.md +87 -0
  140. package/docs/governance/skill-testing.md +356 -0
  141. package/docs/governance/template-authoring.md +53 -0
  142. package/docs/migrations/v0.2.md +51 -0
  143. package/docs/plans/README.md +23 -0
  144. package/docs/plans/bdd-then-tdd-plan.md +1767 -0
  145. package/docs/plans/docs-knowledge-system-plan.md +31 -0
  146. package/docs/plans/spec-kit-sdd-adoption-plan.md +305 -0
  147. package/docs/research/agents-md-best-practices.md +207 -0
  148. package/docs/research/archon-study.md +351 -0
  149. package/docs/research/claude-hooks-study.md +440 -0
  150. package/docs/research/codex-hooks-study.md +624 -0
  151. package/docs/research/everything-claude-code-study.md +252 -0
  152. package/docs/research/from-skills-to-layered-workflow.md +322 -0
  153. package/docs/research/gsd-study.md +69 -0
  154. package/docs/research/kimi-hooks-study.md +274 -0
  155. package/docs/research/mattpocock-skills-comparison.md +429 -0
  156. package/docs/research/mattpocock-skills-study.md +275 -0
  157. package/docs/research/oh-my-codex-study.md +279 -0
  158. package/docs/research/perplexity-agent-skills-design.md +168 -0
  159. package/docs/research/pmc-gstack-skill-study.md +122 -0
  160. package/docs/research/spec-kit-study.md +224 -0
  161. package/docs/research/superpowers-study.md +209 -0
  162. package/docs/roadmap/initial-roadmap.md +53 -0
  163. package/docs/solutions/README.md +45 -0
  164. package/docs/solutions/artifact-nesting-recovery.md +58 -0
  165. package/docs/solutions/session-context-restore-practice.md +67 -0
  166. package/docs/solutions/workflow/version-drift-recovery.md +49 -0
  167. package/docs/solutions/worktree-gate-recovery.md +62 -0
  168. package/docs/specs/README.md +28 -0
  169. package/docs/specs/claude.md +45 -0
  170. package/docs/specs/codex.md +44 -0
  171. package/docs/specs/cursor.md +44 -0
  172. package/hosts/adapters/claude.ts +29 -0
  173. package/hosts/adapters/codex.ts +27 -0
  174. package/hosts/adapters/cursor.ts +27 -0
  175. package/hosts/adapters/kimi.ts +27 -0
  176. package/hosts/claude.ts +23 -0
  177. package/hosts/codex.ts +26 -0
  178. package/hosts/cursor.ts +19 -0
  179. package/hosts/index.ts +33 -0
  180. package/hosts/registry.test.ts +52 -0
  181. package/hosts/registry.ts +57 -0
  182. package/hosts/schema.ts +58 -0
  183. package/package.json +52 -0
  184. package/scripts/browser.ts +185 -0
  185. package/scripts/cleanup.ts +142 -0
  186. package/scripts/commands/artifact.ts +115 -0
  187. package/scripts/commands/autopilot.ts +143 -0
  188. package/scripts/commands/capability.ts +57 -0
  189. package/scripts/commands/config.ts +69 -0
  190. package/scripts/commands/dag.ts +126 -0
  191. package/scripts/commands/feedback.ts +123 -0
  192. package/scripts/commands/gate.ts +291 -0
  193. package/scripts/commands/helpers.ts +126 -0
  194. package/scripts/commands/hook.ts +66 -0
  195. package/scripts/commands/init.ts +515 -0
  196. package/scripts/commands/issue.ts +825 -0
  197. package/scripts/commands/phase.ts +61 -0
  198. package/scripts/commands/preset.ts +159 -0
  199. package/scripts/commands/runtime.ts +199 -0
  200. package/scripts/commands/specify.ts +71 -0
  201. package/scripts/commands/upgrade.ts +243 -0
  202. package/scripts/commands/verify.ts +183 -0
  203. package/scripts/commands/wiki.ts +242 -0
  204. package/scripts/commands/workflow.ts +131 -0
  205. package/scripts/dev-skill.ts +55 -0
  206. package/scripts/discover-skills.ts +116 -0
  207. package/scripts/doctor.ts +410 -0
  208. package/scripts/dogfood-check.ts +125 -0
  209. package/scripts/eval-functional.ts +218 -0
  210. package/scripts/eval.ts +246 -0
  211. package/scripts/gen-skill-docs.ts +201 -0
  212. package/scripts/global-discover.ts +217 -0
  213. package/scripts/governance-check.ts +75 -0
  214. package/scripts/gxpm-check.ts +12 -0
  215. package/scripts/gxpm.ts +216 -0
  216. package/scripts/host-config.ts +62 -0
  217. package/scripts/install-claude-hooks.ts +138 -0
  218. package/scripts/install-codex-hooks.ts +271 -0
  219. package/scripts/install-hooks.ts +128 -0
  220. package/scripts/install-kimi-hooks.ts +92 -0
  221. package/scripts/install-skill.ts +184 -0
  222. package/scripts/phase-artifact-commands.ts +100 -0
  223. package/scripts/post-land-sync.ts +46 -0
  224. package/scripts/scaffold-check.ts +85 -0
  225. package/scripts/skill-naming-check.ts +78 -0
  226. package/scripts/skill-structure-check.ts +157 -0
  227. package/scripts/skills-lock-check.ts +60 -0
  228. package/scripts/sync-markdown-artifacts.ts +172 -0
  229. package/scripts/uninstall.ts +162 -0
  230. package/scripts/version.ts +47 -0
  231. package/scripts/wait-pr-ready.ts +407 -0
  232. package/skills/gxpm/SKILL.md +485 -0
  233. package/skills/gxpm/SKILL.md.tmpl +422 -0
  234. package/skills/gxpm/references/CANON.md +53 -0
  235. package/skills/gxpm/references/key-rules.md +130 -0
  236. package/skills/gxpm-architecture/SKILL.md +106 -0
  237. package/skills/gxpm-architecture/references/DEEPENING.md +37 -0
  238. package/skills/gxpm-architecture/references/INTERFACE-DESIGN.md +44 -0
  239. package/skills/gxpm-autopilot/SKILL.md +116 -0
  240. package/skills/gxpm-autopilot/SKILL.md.tmpl +107 -0
  241. package/skills/gxpm-browser/SKILL.md +105 -0
  242. package/skills/gxpm-browser/SKILL.md.tmpl +41 -0
  243. package/skills/gxpm-browser/references/commands.md +43 -0
  244. package/skills/gxpm-browser/references/evidence-path.md +20 -0
  245. package/skills/gxpm-build/SKILL.md +78 -0
  246. package/skills/gxpm-cleanup/SKILL.md +76 -0
  247. package/skills/gxpm-debug-issue/SKILL.md +39 -0
  248. package/skills/gxpm-diagnose/SKILL.md +220 -0
  249. package/skills/gxpm-diagnose/SKILL.md.tmpl +31 -0
  250. package/skills/gxpm-diagnose/references/feedback-loop.md +34 -0
  251. package/skills/gxpm-diagnose/references/feedback-loops.md +43 -0
  252. package/skills/gxpm-diagnose/references/phases.md +60 -0
  253. package/skills/gxpm-eval/SKILL.md +78 -0
  254. package/skills/gxpm-explore-codebase/SKILL.md +36 -0
  255. package/skills/gxpm-explore-codebase/scripts/summarize-communities.ts +51 -0
  256. package/skills/gxpm-feedback/SKILL.md +122 -0
  257. package/skills/gxpm-grill/SKILL.md +159 -0
  258. package/skills/gxpm-grill/SKILL.md.tmpl +77 -0
  259. package/skills/gxpm-grill/references/documentation-templates.md +56 -0
  260. package/skills/gxpm-grill/references/process.md +25 -0
  261. package/skills/gxpm-handoff/SKILL.md +112 -0
  262. package/skills/gxpm-hygiene/SKILL.md +69 -0
  263. package/skills/gxpm-implementer/SKILL.md +142 -0
  264. package/skills/gxpm-implementer/SKILL.md.tmpl +141 -0
  265. package/skills/gxpm-linear/SKILL.md +282 -0
  266. package/skills/gxpm-linear/SKILL.md.tmpl +86 -0
  267. package/skills/gxpm-linear/references/commands.md +75 -0
  268. package/skills/gxpm-linear/references/workflows.md +120 -0
  269. package/skills/gxpm-planning/SKILL.md +134 -0
  270. package/skills/gxpm-prototype/SKILL.md +64 -0
  271. package/skills/gxpm-refactor-safely/SKILL.md +62 -0
  272. package/skills/gxpm-review-army/SKILL.md +117 -0
  273. package/skills/gxpm-review-changes/SKILL.md +36 -0
  274. package/skills/gxpm-setup/SKILL.md +101 -0
  275. package/skills/gxpm-specifier/SKILL.md +135 -0
  276. package/skills/gxpm-tdd/SKILL.md +187 -0
  277. package/skills/gxpm-tdd/references/interface-design.md +23 -0
  278. package/skills/gxpm-tdd/references/mocking.md +27 -0
  279. package/skills/gxpm-tdd/references/red-green-refactor.md +61 -0
  280. package/skills/gxpm-tdd/references/troubleshooting.md +28 -0
  281. package/skills/gxpm-tdd/references/workflow.md +50 -0
  282. package/skills/gxpm-tdd/testing-anti-patterns.tmpl +304 -0
  283. package/skills/gxpm-triage/SKILL.md +160 -0
  284. package/skills/gxpm-verify/SKILL.md +107 -0
  285. package/skills/gxpm-write-skill/SKILL.md +131 -0
  286. package/skills/gxpm-zoom-out/SKILL.md +69 -0
  287. package/skills/maintain-hygiene-skills-lock/SKILL.md +54 -0
  288. package/skills/maintain-hygiene-skills-lock/SKILL.md.tmpl +53 -0
  289. package/templates/constitution-template.md +63 -0
  290. package/templates/hooks/gxpm-commit-msg +16 -0
  291. package/templates/hooks/gxpm-post-checkout +19 -0
  292. package/templates/hooks/gxpm-post-commit +7 -0
  293. package/templates/hooks/gxpm-post-merge +29 -0
  294. package/templates/hooks/gxpm-pre-commit +39 -0
  295. package/templates/hooks/gxpm-pre-push +33 -0
  296. package/templates/plan-template.md.tmpl +46 -0
  297. package/templates/spec-template.md.tmpl +63 -0
  298. package/templates/specify-stub.tmpl +22 -0
  299. package/templates/tasks-template.md.tmpl +32 -0
@@ -0,0 +1,330 @@
1
+ /**
2
+ * WorktreeInitPipeline — pluggable initialization for gxpm-managed worktrees.
3
+ *
4
+ * Problem: gxpm creates worktrees via IsolationResolver but only does minimal
5
+ * post-creation setup (.gxpm symlink, node_modules symlink, owner marker).
6
+ * Real projects (Gxgen, etc.) need much richer initialization:
7
+ * - node_modules overlay mode
8
+ * - .env symlinks + .env.local generation
9
+ * - multi-service port allocation with occupancy checks
10
+ * - warp.md / launch.json generation
11
+ * - git hooks, tool indexes, baseline freshness checks
12
+ *
13
+ * Solution: a configurable pipeline of InitSteps. Projects opt-in via
14
+ * `.gxpm/config.json` `worktree.initSteps`. Each step is self-contained,
15
+ * reads its own configuration from the gxpm config registry, and reports
16
+ * warnings/errors without blocking the pipeline unless configured to do so.
17
+ */
18
+
19
+ import { existsSync, lstatSync, mkdirSync, readFileSync, readlinkSync, realpathSync, rmSync, symlinkSync, writeFileSync } from "node:fs";
20
+ import { dirname, join, relative, resolve } from "node:path";
21
+ import { createHash } from "node:crypto";
22
+ import { getResolvedConfigValue } from "./config";
23
+
24
+ // ---------------------------------------------------------------------------
25
+ // Types
26
+ // ---------------------------------------------------------------------------
27
+
28
+ export interface PortAllocation {
29
+ slot: number;
30
+ webPort: number;
31
+ serverPort: number;
32
+ studioPort: number;
33
+ queuePrefix: string;
34
+ }
35
+
36
+ export interface WorktreeInitContext {
37
+ /** Absolute path to the canonical (main) repo checkout. */
38
+ canonicalRepoPath: string;
39
+ /** Absolute path to the worktree being initialized. */
40
+ worktreePath: string;
41
+ /** Git branch name for the worktree. */
42
+ branchName: string;
43
+ /** gxpm issue id. */
44
+ issueId: string;
45
+ /** Base branch used to create the worktree (if known). */
46
+ baseBranch?: string;
47
+ /** Base commit SHA used to create the worktree (if known). */
48
+ baseSha?: string;
49
+ /** Populated by the port-allocation step for downstream consumers. */
50
+ ports?: PortAllocation;
51
+ }
52
+
53
+ export interface WorktreeInitStepResult {
54
+ ok: boolean;
55
+ warnings?: string[];
56
+ error?: string;
57
+ }
58
+
59
+ export interface WorktreeInitStep {
60
+ name: string;
61
+ run(ctx: WorktreeInitContext): Promise<WorktreeInitStepResult> | WorktreeInitStepResult;
62
+ }
63
+
64
+ export interface WorktreeInitPipelineResult {
65
+ ok: boolean;
66
+ warnings: string[];
67
+ errors: string[];
68
+ stepsRun: string[];
69
+ }
70
+
71
+ // ---------------------------------------------------------------------------
72
+ // Pipeline
73
+ // ---------------------------------------------------------------------------
74
+
75
+ export class WorktreeInitPipeline {
76
+ private steps: WorktreeInitStep[] = [];
77
+
78
+ add(step: WorktreeInitStep): this {
79
+ this.steps.push(step);
80
+ return this;
81
+ }
82
+
83
+ async run(ctx: WorktreeInitContext): Promise<WorktreeInitPipelineResult> {
84
+ const warnings: string[] = [];
85
+ const errors: string[] = [];
86
+ const stepsRun: string[] = [];
87
+
88
+ for (const step of this.steps) {
89
+ try {
90
+ const result = await step.run(ctx);
91
+ stepsRun.push(step.name);
92
+ if (result.warnings) warnings.push(...result.warnings);
93
+ if (!result.ok) {
94
+ if (result.error) errors.push(`[${step.name}] ${result.error}`);
95
+ else errors.push(`[${step.name}] failed`);
96
+ }
97
+ } catch (err) {
98
+ stepsRun.push(step.name);
99
+ const message = err instanceof Error ? err.message : String(err);
100
+ errors.push(`[${step.name}] ${message}`);
101
+ }
102
+ }
103
+
104
+ return { ok: errors.length === 0, warnings, errors, stepsRun };
105
+ }
106
+ }
107
+
108
+ // ---------------------------------------------------------------------------
109
+ // Built-in step registry
110
+ // ---------------------------------------------------------------------------
111
+
112
+ const BUILTIN_STEPS: Record<string, (() => WorktreeInitStep) | WorktreeInitStep> = {};
113
+
114
+ export function registerBuiltinStep(name: string, step: (() => WorktreeInitStep) | WorktreeInitStep): void {
115
+ BUILTIN_STEPS[name] = step;
116
+ }
117
+
118
+ export function getBuiltinStepNames(): string[] {
119
+ return Object.keys(BUILTIN_STEPS);
120
+ }
121
+
122
+ export function resolveStep(name: string): WorktreeInitStep | undefined {
123
+ const entry = BUILTIN_STEPS[name];
124
+ if (!entry) return undefined;
125
+ return typeof entry === "function" ? entry() : entry;
126
+ }
127
+
128
+ // ---------------------------------------------------------------------------
129
+ // Pipeline factory
130
+ // ---------------------------------------------------------------------------
131
+
132
+ export interface CreatePipelineInput {
133
+ /** Explicit step names; if omitted reads from config. */
134
+ steps?: string[];
135
+ root?: string;
136
+ }
137
+
138
+ export function createPipeline(input: CreatePipelineInput = {}): WorktreeInitPipeline {
139
+ const root = input.root ?? process.cwd();
140
+ const stepNames =
141
+ input.steps ??
142
+ (() => {
143
+ const cfg = getResolvedConfigValue({ root, key: "worktree.initSteps" });
144
+ return Array.isArray(cfg.value) ? (cfg.value as string[]) : ["owner-marker", "issue-context"];
145
+ })();
146
+
147
+ const pipeline = new WorktreeInitPipeline();
148
+ for (const name of stepNames) {
149
+ const step = resolveStep(name);
150
+ if (step) {
151
+ pipeline.add(step);
152
+ } else {
153
+ // Unknown step: inject a no-op that warns
154
+ pipeline.add({
155
+ name: `unknown:${name}`,
156
+ run: () => ({
157
+ ok: false,
158
+ warnings: [`Unknown worktree init step "${name}". Available: ${getBuiltinStepNames().join(", ")}`],
159
+ }),
160
+ });
161
+ }
162
+ }
163
+ return pipeline;
164
+ }
165
+
166
+ // ---------------------------------------------------------------------------
167
+ // High-level runner
168
+ // ---------------------------------------------------------------------------
169
+
170
+ export async function runWorktreeInit(
171
+ ctx: WorktreeInitContext,
172
+ input?: CreatePipelineInput,
173
+ ): Promise<WorktreeInitPipelineResult> {
174
+ const pipeline = createPipeline(input);
175
+ return pipeline.run(ctx);
176
+ }
177
+
178
+ // ---------------------------------------------------------------------------
179
+ // Shared helpers used by multiple steps
180
+ // ---------------------------------------------------------------------------
181
+
182
+ export function ensureSymlink(src: string, dst: string): { ok: boolean; warning?: string } {
183
+ if (resolve(src) === resolve(dst)) return { ok: true };
184
+
185
+ if (existsSync(dst)) {
186
+ const stat = lstatSync(dst);
187
+ if (stat.isSymbolicLink()) {
188
+ try {
189
+ const current = readlinkSync(dst);
190
+ if (resolve(dst, current) === resolve(src) || realpathSync(resolve(dst, current)) === realpathSync(src)) {
191
+ return { ok: true };
192
+ }
193
+ rmSync(dst, { force: true });
194
+ } catch {
195
+ rmSync(dst, { force: true });
196
+ }
197
+ } else {
198
+ return { ok: false, warning: `overlay conflict: ${dst} exists as real file/directory; skipped` };
199
+ }
200
+ }
201
+
202
+ mkdirSync(dirname(dst), { recursive: true });
203
+ symlinkSync(src, dst, "dir");
204
+ return { ok: true };
205
+ }
206
+
207
+ export function setEnvVar(filePath: string, key: string, value: string): void {
208
+ mkdirSync(dirname(filePath), { recursive: true });
209
+ let content = "";
210
+ try {
211
+ content = readFileSync(filePath, "utf8");
212
+ } catch {
213
+ // file does not exist yet
214
+ }
215
+
216
+ const lines = content.split("\n");
217
+ let found = false;
218
+ for (let i = 0; i < lines.length; i++) {
219
+ const line = lines[i];
220
+ if (line.startsWith(`${key}=`)) {
221
+ lines[i] = `${key}=${value}`;
222
+ found = true;
223
+ break;
224
+ }
225
+ }
226
+ if (!found) {
227
+ lines.push(`${key}=${value}`);
228
+ }
229
+ writeFileSync(filePath, lines.join("\n").replace(/\n+$/, "") + "\n");
230
+ }
231
+
232
+ export function readEnvFile(path: string): Record<string, string> {
233
+ const result: Record<string, string> = {};
234
+ if (!existsSync(path)) return result;
235
+ const content = readFileSync(path, "utf8");
236
+ for (const line of content.split("\n")) {
237
+ const trimmed = line.trim();
238
+ if (!trimmed || trimmed.startsWith("#")) continue;
239
+ const eq = trimmed.indexOf("=");
240
+ if (eq === -1) continue;
241
+ result[trimmed.slice(0, eq)] = trimmed.slice(eq + 1);
242
+ }
243
+ return result;
244
+ }
245
+
246
+ export function safeWorktreeSlug(raw: string): string {
247
+ let slug = raw
248
+ .toLowerCase()
249
+ .replace(/[^a-z0-9]+/g, "_")
250
+ .replace(/^_+|_+$/g, "")
251
+ .replace(/_+/g, "_")
252
+ .slice(0, 48);
253
+ if (!slug) {
254
+ slug = String(createHash("sha256").update(raw).digest("hex").slice(0, 8));
255
+ }
256
+ return slug;
257
+ }
258
+
259
+ export function hashSlot(input: string, maxSlot: number): number {
260
+ const hash = createHash("sha256").update(input).digest();
261
+ return (hash.readUInt32BE(0) % maxSlot) + 1; // 1-based
262
+ }
263
+
264
+ export function checkPortAvailable(port: number, worktreePath: string): boolean {
265
+ try {
266
+ const result = Bun.spawnSync({
267
+ cmd: ["lsof", "-nP", "-iTCP:" + String(port), "-sTCP:LISTEN"],
268
+ stdout: "pipe",
269
+ stderr: "pipe",
270
+ });
271
+ if (result.exitCode !== 0) return true; // lsof found nothing
272
+
273
+ const output = result.stdout.toString().trim();
274
+ if (!output) return true;
275
+
276
+ // Parse PIDs and check if they belong to this worktree
277
+ const lines = output.split("\n").slice(1); // skip header
278
+ for (const line of lines) {
279
+ const pid = line.trim().split(/\s+/)[1];
280
+ if (!pid) continue;
281
+ try {
282
+ const cwdResult = Bun.spawnSync({
283
+ cmd: ["lsof", "-a", "-p", pid, "-d", "cwd", "-Fn"],
284
+ stdout: "pipe",
285
+ stderr: "pipe",
286
+ });
287
+ if (cwdResult.exitCode === 0) {
288
+ const cwdLine = cwdResult.stdout.toString().split("\n").find((l) => l.startsWith("n"));
289
+ const cwd = cwdLine ? cwdLine.slice(1) : "";
290
+ if (cwd === worktreePath || cwd.startsWith(worktreePath + "/")) {
291
+ continue; // belongs to this worktree, that's fine
292
+ }
293
+ }
294
+ } catch {
295
+ // ignore lsof failure
296
+ }
297
+ return false; // someone else owns this port
298
+ }
299
+ return true;
300
+ } catch {
301
+ // lsof not available → assume available (best-effort)
302
+ return true;
303
+ }
304
+ }
305
+
306
+ export function appendGitExcludeEntry(repoRoot: string, entry: string): void {
307
+ try {
308
+ const result = Bun.spawnSync({
309
+ cmd: ["git", "rev-parse", "--git-path", "info/exclude"],
310
+ cwd: repoRoot,
311
+ stdout: "pipe",
312
+ stderr: "pipe",
313
+ });
314
+ if (result.exitCode !== 0) return;
315
+ const excludeFile = result.stdout.toString().trim();
316
+ if (!excludeFile) return;
317
+ mkdirSync(dirname(excludeFile), { recursive: true });
318
+ let content = "";
319
+ try {
320
+ content = readFileSync(excludeFile, "utf8");
321
+ } catch {
322
+ // file may not exist
323
+ }
324
+ if (!content.split("\n").includes(entry)) {
325
+ writeFileSync(excludeFile, (content ? content.replace(/\n+$/, "") + "\n" : "") + entry + "\n");
326
+ }
327
+ } catch {
328
+ // best-effort
329
+ }
330
+ }
@@ -0,0 +1,143 @@
1
+ /**
2
+ * Worktree owner marker and passive context recovery.
3
+ *
4
+ * Problem: After multi-turn context compaction, an AI agent may forget the
5
+ * current issue and worktree. These files let the agent recover passively
6
+ * from the filesystem without needing to remember the issue id.
7
+ */
8
+
9
+ import { existsSync, readFileSync, writeFileSync } from "node:fs";
10
+ import { join } from "node:path";
11
+
12
+ export interface WorktreeOwner {
13
+ ownerIssueId: string;
14
+ linkedIssues: string[];
15
+ createdAt: string;
16
+ /** Current issue phase (v2) */
17
+ currentPhase?: string;
18
+ /** Issue title or checkpoint title (v2) */
19
+ title?: string;
20
+ /** Last update timestamp (v2) */
21
+ updatedAt?: string;
22
+ /** Git branch name (v2) */
23
+ branchName?: string;
24
+ /** Absolute workspace path (v2) */
25
+ workspacePath?: string;
26
+ }
27
+
28
+ export function readWorktreeOwner(cwd: string): WorktreeOwner | null {
29
+ try {
30
+ const path = join(cwd, ".gxpm-worktree-owner.json");
31
+ if (!existsSync(path)) return null;
32
+ const raw = JSON.parse(readFileSync(path, "utf8")) as Record<string, unknown>;
33
+ const ownerIssueId = typeof raw.ownerIssueId === "string" ? raw.ownerIssueId : "";
34
+ const linkedIssues = Array.isArray(raw.linkedIssues)
35
+ ? raw.linkedIssues.filter((item): item is string => typeof item === "string")
36
+ : [];
37
+ const createdAt = typeof raw.createdAt === "string" ? raw.createdAt : "";
38
+ if (!ownerIssueId || !createdAt) return null;
39
+ return {
40
+ ownerIssueId,
41
+ linkedIssues,
42
+ createdAt,
43
+ currentPhase: typeof raw.currentPhase === "string" ? raw.currentPhase : undefined,
44
+ title: typeof raw.title === "string" ? raw.title : undefined,
45
+ updatedAt: typeof raw.updatedAt === "string" ? raw.updatedAt : undefined,
46
+ branchName: typeof raw.branchName === "string" ? raw.branchName : undefined,
47
+ workspacePath: typeof raw.workspacePath === "string" ? raw.workspacePath : undefined,
48
+ };
49
+ } catch {
50
+ return null;
51
+ }
52
+ }
53
+
54
+ export function writeWorktreeOwnerMarker(
55
+ workspacePath: string,
56
+ marker: Omit<WorktreeOwner, "createdAt"> & Partial<Pick<WorktreeOwner, "createdAt">>,
57
+ ): void {
58
+ const fullMarker: WorktreeOwner = {
59
+ linkedIssues: marker.linkedIssues,
60
+ createdAt: marker.createdAt ?? new Date().toISOString(),
61
+ ownerIssueId: marker.ownerIssueId,
62
+ currentPhase: marker.currentPhase,
63
+ title: marker.title,
64
+ updatedAt: marker.updatedAt ?? new Date().toISOString(),
65
+ branchName: marker.branchName,
66
+ workspacePath: marker.workspacePath,
67
+ };
68
+ writeFileSync(
69
+ join(workspacePath, ".gxpm-worktree-owner.json"),
70
+ `${JSON.stringify(fullMarker, null, 2)}\n`,
71
+ );
72
+ }
73
+
74
+ export interface IssueContextMdData {
75
+ issueId: string;
76
+ title?: string;
77
+ currentPhase: string;
78
+ nextPhase?: string;
79
+ branchName?: string;
80
+ workspacePath: string;
81
+ updatedAt: string;
82
+ resumeHint?: string;
83
+ }
84
+
85
+ export function writeIssueContextMd(workspacePath: string, data: IssueContextMdData): void {
86
+ const lines: string[] = [
87
+ `# ISSUE_CONTEXT — ${data.issueId}`,
88
+ ``,
89
+ `> 这是 gxpm 自动生成的上下文恢复文件。`,
90
+ `> 如果 AI 代理忘记了当前正在处理的 issue,请阅读此文件。`,
91
+ ``,
92
+ `- **Issue ID**: \`${data.issueId}\``,
93
+ `- **当前阶段**: \`${data.currentPhase}\``,
94
+ ];
95
+ if (data.title) {
96
+ lines.push(`- **标题**: ${data.title}`);
97
+ }
98
+ if (data.branchName) {
99
+ lines.push(`- **分支**: \`${data.branchName}\``);
100
+ }
101
+ lines.push(`- **工作目录**: \`${data.workspacePath}\``);
102
+ lines.push(`- **更新时间**: ${data.updatedAt}`);
103
+ lines.push(``);
104
+ lines.push(`## 快速恢复`);
105
+ lines.push(``);
106
+ lines.push(`如果你忘记了当前 issue,运行以下命令恢复上下文:`);
107
+ lines.push(``);
108
+ lines.push(`\`\`\`bash`);
109
+ lines.push(`gxpm issue context --auto`);
110
+ lines.push(`\`\`\``);
111
+ lines.push(``);
112
+ if (data.nextPhase) {
113
+ lines.push(`## 下一步`);
114
+ lines.push(``);
115
+ lines.push(`下一阶段: \`${data.nextPhase}\``);
116
+ lines.push(``);
117
+ }
118
+ if (data.resumeHint) {
119
+ lines.push(`## 续做提示`);
120
+ lines.push(``);
121
+ lines.push(data.resumeHint);
122
+ lines.push(``);
123
+ }
124
+ lines.push(`---`);
125
+ lines.push(`*此文件由 gxpm 自动生成,请勿手动编辑。*`);
126
+
127
+ writeFileSync(join(workspacePath, "ISSUE_CONTEXT.md"), lines.join("\n") + "\n");
128
+ }
129
+
130
+ export function removeIssueContextMd(workspacePath: string): void {
131
+ try {
132
+ const path = join(workspacePath, "ISSUE_CONTEXT.md");
133
+ if (existsSync(path)) {
134
+ // Leave the file but mark it as stale so the agent doesn't trust it
135
+ const content = readFileSync(path, "utf8");
136
+ if (!content.includes("[STALE]")) {
137
+ writeFileSync(path, `# [STALE] ${content.replace(/^# /, "")}`, "utf8");
138
+ }
139
+ }
140
+ } catch {
141
+ // best-effort
142
+ }
143
+ }
@@ -0,0 +1,98 @@
1
+ # GXPM Verification Checklist
2
+
3
+ Run these checks after installing or upgrading gxpm to confirm everything works.
4
+
5
+ ## Check #1: Runtime
6
+
7
+ ```bash
8
+ gxpm doctor --json
9
+ ```
10
+
11
+ Expected: `status` is `healthy` or `warnings`. `health_score` >= 80.
12
+
13
+ If `status` is `error`, run `gxpm doctor --fix` and retry.
14
+
15
+ ## Check #2: CLI Commands
16
+
17
+ ```bash
18
+ gxpm --version
19
+ gxpm config list
20
+ gxpm session-id
21
+ ```
22
+
23
+ All should return valid output without error.
24
+
25
+ ## Check #3: Git Hooks
26
+
27
+ Make any commit in a repo where gxpm hooks are installed:
28
+
29
+ ```bash
30
+ git commit --allow-empty -m "test: verify gxpm hooks"
31
+ ```
32
+
33
+ The commit should succeed. If pre-commit fails, check `.githooks/gxpm-pre-commit` output.
34
+
35
+ ## Check #4: Issue Lifecycle
36
+
37
+ ```bash
38
+ gxpm issue create --auto-id
39
+ ```
40
+
41
+ Note the created issue ID (e.g., `GXPM-123`). Then:
42
+
43
+ ```bash
44
+ gxpm issue transition GXPM-123 plan
45
+ gxpm issue transition GXPM-123 dispatch
46
+ gxpm issue transition GXPM-123 implement
47
+ ```
48
+
49
+ Each transition should succeed. Inspect `.gxpm/issues/GXPM-123/state.json` to confirm phase progression.
50
+
51
+ ## Check #5: Worktree (if worktree.enforcement != forbidden)
52
+
53
+ ```bash
54
+ git worktree add ../test-worktree main
55
+ ```
56
+
57
+ Should succeed. Clean up:
58
+
59
+ ```bash
60
+ git worktree remove ../test-worktree
61
+ ```
62
+
63
+ ## Check #6: Linear Sync (if configured)
64
+
65
+ If `sync.provider` is `linear`:
66
+
67
+ ```bash
68
+ gxpm doctor --json | jq '.checks[] | select(.name=="linear_connectivity")'
69
+ ```
70
+
71
+ Expected: `status: "ok"`. If `warn`, check `LINEAR_API_KEY` env var.
72
+
73
+ Create an issue and confirm it appears in Linear (may take a few seconds):
74
+
75
+ ```bash
76
+ gxpm issue create --auto-id
77
+ gxpm issue sync <id>
78
+ ```
79
+
80
+ ## Check #7: Skill Host Discovery
81
+
82
+ ```bash
83
+ gxpm doctor --json | jq '.checks[] | select(.name=="skill_installation")'
84
+ ```
85
+
86
+ Expected: `status: "ok"`. If `warn`, run:
87
+
88
+ ```bash
89
+ gxpm init --install-skill --host all
90
+ ```
91
+
92
+ ## One-Command Smoke Test
93
+
94
+ ```bash
95
+ gxpm verify
96
+ ```
97
+
98
+ This runs all checks above (except Check #6 which requires manual confirmation in Linear UI). Result should be `passed`.
@@ -0,0 +1,113 @@
1
+ # gxpm Installation Guide for AI Agents
2
+
3
+ Read this entire file, then follow the steps. Target: ~10 minutes to a fully working project management runtime.
4
+
5
+ ## Step 0: If you are not Claude Code
6
+
7
+ Read `AGENTS.md` at the repo root first. It's the non-Claude-agent operating protocol (install, read order, trust boundary, common tasks). Claude Code reads `CLAUDE.md` automatically and can skip ahead.
8
+
9
+ If you fetched this file by URL without cloning yet, the companion files live at:
10
+ - `https://raw.githubusercontent.com/laozhong86/gxpm/main/AGENTS.md` — start here
11
+ - `https://raw.githubusercontent.com/laozhong86/gxpm/main/CONTEXT.md` — shared language / glossary
12
+
13
+ ## Step 1: Install gxpm
14
+
15
+ ```bash
16
+ git clone https://github.com/laozhong86/gxpm.git ~/gxpm && cd ~/gxpm
17
+ curl -fsSL https://bun.sh/install | bash
18
+ export PATH="$HOME/.bun/bin:$PATH"
19
+ bun install && bun link
20
+ ```
21
+
22
+ Verify: `gxpm --version` should print a version number. If `gxpm` is not found, restart the shell or add the PATH export to the shell profile.
23
+
24
+ ## Step 2: Initialize a Project
25
+
26
+ Navigate to the project you want to manage with gxpm (or stay in `~/gxpm` to manage gxpm itself):
27
+
28
+ ```bash
29
+ cd /path/to/your/project
30
+ gxpm init
31
+ ```
32
+
33
+ This will:
34
+ 1. Create `.gxpm/` directory structure
35
+ 2. Install git hooks (pre-commit, commit-msg, pre-push, post-merge, post-checkout)
36
+ 3. Install Codex hooks (if `.codex/` detected)
37
+ 4. Install gxpm skills to all detected host adapters (Claude, Codex, Cursor)
38
+ 5. Initialize `.gxpm/config.json` with sensible defaults
39
+
40
+ For non-interactive / CI mode:
41
+ ```bash
42
+ gxpm init --non-interactive --target /path/to/project
43
+ ```
44
+
45
+ ## Step 3: Configure
46
+
47
+ Review and adjust configuration:
48
+
49
+ ```bash
50
+ gxpm config list
51
+ ```
52
+
53
+ Key settings to consider:
54
+ - `sync.provider`: `linear`, `github`, or `none`
55
+ - `sync.linearTeamId` / `sync.linearTeamKey`: if using Linear
56
+ - `worktree.enforcement`: `optional` (default), `required`, or `forbidden`
57
+
58
+ Set via:
59
+ ```bash
60
+ gxpm config set sync.provider linear
61
+ gxpm config set sync.linearTeamId your-team-uuid --global
62
+ ```
63
+
64
+ ## Step 4: Verify
65
+
66
+ Run the verification suite:
67
+
68
+ ```bash
69
+ gxpm doctor --json
70
+ gxpm verify
71
+ ```
72
+
73
+ All checks should pass. If any fail, follow the remediation hints printed.
74
+
75
+ ## Step 5: Create Your First Issue
76
+
77
+ ```bash
78
+ gxpm issue create --auto-id
79
+ ```
80
+
81
+ Follow the triage flow. The issue state will be persisted under `.gxpm/issues/<id>/`.
82
+
83
+ ## Step 6: Load Skills
84
+
85
+ Read `skills/gxpm/SKILL.md`. This is the skill dispatcher. It tells you which skill to read for any task. Save this to your memory permanently.
86
+
87
+ The three most important skills to adopt immediately:
88
+
89
+ 1. **gxpm-triage** — intake and classify incoming work
90
+ 2. **gxpm-planning** — scope confirmation and implementation planning
91
+ 3. **gxpm** — master dispatch for issue lifecycle, phase gates, and artifact management
92
+
93
+ ## Step 7: Recurring Jobs
94
+
95
+ Set up using your platform's scheduler:
96
+
97
+ - **Auto-update check** (daily): `gxpm-update-check` (tell user, never auto-install)
98
+ - **Weekly health check**: `gxpm doctor --json && gxpm verify`
99
+
100
+ ## Upgrade
101
+
102
+ ```bash
103
+ cd ~/gxpm && git pull origin main && bun install
104
+ gxpm upgrade
105
+ ```
106
+
107
+ Then read `docs/migrations/v*.md` for any versions you skipped and run any backfill steps listed.
108
+
109
+ ## Troubleshooting
110
+
111
+ - `gxpm doctor --fix` auto-repairs common issues (missing hooks, stale skill docs)
112
+ - `gxpm doctor --json` gives machine-readable output for agent consumption
113
+ - Read `docs/GXPM_VERIFY.md` for the full verification checklist