@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.
- package/AGENTS.md +148 -0
- package/CANON.md +53 -0
- package/CLAUDE.md +60 -0
- package/CONTEXT.md +49 -0
- package/DEBUG.md +59 -0
- package/ISSUE_CONTEXT.md +25 -0
- package/README.md +143 -0
- package/VERSION +1 -0
- package/agents/cleanup-auditor/cleanup-auditor.md +56 -0
- package/agents/grill-master.md +26 -0
- package/agents/implementer.md +32 -0
- package/agents/review-army/accessibility-reviewer.md +54 -0
- package/agents/review-army/code-quality-reviewer.md +54 -0
- package/agents/review-army/security-reviewer.md +56 -0
- package/agents/review-army/spec-compliance-reviewer.md +51 -0
- package/agents/review-army/test-reviewer.md +55 -0
- package/agents/reviewer.md +59 -0
- package/agents/ship-audit-army/docs-auditor.md +53 -0
- package/agents/ship-audit-army/performance-auditor.md +52 -0
- package/agents/ship-audit-army/security-auditor.md +52 -0
- package/agents/specifier.md +55 -0
- package/agents/triage-officer.md +27 -0
- package/bin/gxpm +17 -0
- package/bin/gxpm-browser +17 -0
- package/bin/gxpm-config +15 -0
- package/bin/gxpm-eval +13 -0
- package/bin/gxpm-global-discover +15 -0
- package/bin/gxpm-init +38 -0
- package/bin/gxpm-investigate +194 -0
- package/bin/gxpm-uninstall +15 -0
- package/bin/gxpm-update-check +165 -0
- package/commands/build.md +40 -0
- package/commands/help.md +53 -0
- package/commands/plan.md +34 -0
- package/commands/refine.md +46 -0
- package/commands/review.md +34 -0
- package/commands/ship.md +37 -0
- package/core/ac-check.ts +20 -0
- package/core/agent-runtime.ts +363 -0
- package/core/artifact-validator.ts +151 -0
- package/core/artifacts.ts +313 -0
- package/core/autopilot.ts +250 -0
- package/core/capabilities.ts +779 -0
- package/core/checkpoint.ts +370 -0
- package/core/cleanup.ts +32 -0
- package/core/command-probe.ts +82 -0
- package/core/config.ts +533 -0
- package/core/contracts/behavior-spec.schema.ts +38 -0
- package/core/contracts/converter.ts +61 -0
- package/core/contracts/host.ts +43 -0
- package/core/converters/converter.ts +93 -0
- package/core/converters/index.ts +8 -0
- package/core/converters/managed-artifact.ts +119 -0
- package/core/converters/parser.ts +159 -0
- package/core/converters/template-renderer.ts +35 -0
- package/core/converters/writer.ts +61 -0
- package/core/dag-executor.ts +426 -0
- package/core/dag-loader.ts +292 -0
- package/core/dag-schemas.ts +150 -0
- package/core/dispatch.ts +125 -0
- package/core/evidence.ts +148 -0
- package/core/gate.ts +269 -0
- package/core/hook-engine.ts +566 -0
- package/core/host-probe.ts +64 -0
- package/core/implement.ts +16 -0
- package/core/isolation-errors.ts +174 -0
- package/core/isolation-resolver.ts +921 -0
- package/core/issue-context.ts +381 -0
- package/core/issue-readiness.ts +457 -0
- package/core/issue-sync.ts +427 -0
- package/core/issues.ts +132 -0
- package/core/land.ts +108 -0
- package/core/orchestrator.ts +54 -0
- package/core/phase-artifact.ts +32 -0
- package/core/phase-gates.ts +130 -0
- package/core/phase-rewind.ts +94 -0
- package/core/plan-lint.ts +61 -0
- package/core/plan.ts +77 -0
- package/core/port-allocation.ts +50 -0
- package/core/pr-check.ts +15 -0
- package/core/preset-system/preset-resolver.ts +221 -0
- package/core/project-init-status.ts +127 -0
- package/core/qa.ts +15 -0
- package/core/resilience.ts +165 -0
- package/core/runs.ts +288 -0
- package/core/safe-path.test.ts +80 -0
- package/core/safe-path.ts +60 -0
- package/core/sdd-gate.test.ts +98 -0
- package/core/sdd-gate.ts +134 -0
- package/core/self-review.ts +62 -0
- package/core/session.ts +70 -0
- package/core/ship.ts +86 -0
- package/core/specify.ts +173 -0
- package/core/state.ts +1002 -0
- package/core/template-engine.ts +152 -0
- package/core/template-resolver.test.ts +70 -0
- package/core/template-resolver.ts +156 -0
- package/core/triage.ts +26 -0
- package/core/verify.ts +15 -0
- package/core/wiki-native.ts +2423 -0
- package/core/wiki.ts +27 -0
- package/core/workflow-event-emitter.ts +163 -0
- package/core/workflows/engine.ts +273 -0
- package/core/workflows/expressions.ts +76 -0
- package/core/workflows/index.ts +38 -0
- package/core/workflows/steps/command.ts +43 -0
- package/core/workflows/steps/gate.ts +47 -0
- package/core/workflows/steps/gxpm.ts +44 -0
- package/core/workflows/steps/linear.ts +31 -0
- package/core/workflows/steps/shell.ts +65 -0
- package/core/workflows/types.ts +62 -0
- package/core/workspace-runtime.ts +227 -0
- package/core/worktree-init-steps.ts +647 -0
- package/core/worktree-init.ts +330 -0
- package/core/worktree-owner.ts +143 -0
- package/docs/GXPM_VERIFY.md +98 -0
- package/docs/INSTALL_FOR_AGENTS.md +113 -0
- package/docs/README.md +57 -0
- package/docs/adr/adr-005-multi-platform-skill-converter.md +72 -0
- package/docs/agents/domain.md +30 -0
- package/docs/agents/issue-tracker.md +30 -0
- package/docs/agents/triage-labels.md +32 -0
- package/docs/architecture/gxpm-architecture-diagram.md +265 -0
- package/docs/architecture/gxpm-current-architecture.md +175 -0
- package/docs/architecture/gxpm-current-flow.md +278 -0
- package/docs/architecture/gxpm-replacement-architecture.md +211 -0
- package/docs/architecture/gxpm-target-architecture.md +449 -0
- package/docs/architecture/gxpm-v0-contract.md +311 -0
- package/docs/architecture/layered-workflow-boundaries.md +193 -0
- package/docs/architecture/preset-system.md +126 -0
- package/docs/architecture/scaffold-northstar.md +23 -0
- package/docs/brainstorms/2026-05-14-bdd-then-tdd-design.md +320 -0
- package/docs/brainstorms/README.md +22 -0
- package/docs/brainstorms/docs-knowledge-system-requirements.md +29 -0
- package/docs/governance/beta-skill-promotion.md +39 -0
- package/docs/governance/development-contract.md +144 -0
- package/docs/governance/gherkin-style.md +90 -0
- package/docs/governance/host-adapter.md +56 -0
- package/docs/governance/skill-authoring.md +87 -0
- package/docs/governance/skill-testing.md +356 -0
- package/docs/governance/template-authoring.md +53 -0
- package/docs/migrations/v0.2.md +51 -0
- package/docs/plans/README.md +23 -0
- package/docs/plans/bdd-then-tdd-plan.md +1767 -0
- package/docs/plans/docs-knowledge-system-plan.md +31 -0
- package/docs/plans/spec-kit-sdd-adoption-plan.md +305 -0
- package/docs/research/agents-md-best-practices.md +207 -0
- package/docs/research/archon-study.md +351 -0
- package/docs/research/claude-hooks-study.md +440 -0
- package/docs/research/codex-hooks-study.md +624 -0
- package/docs/research/everything-claude-code-study.md +252 -0
- package/docs/research/from-skills-to-layered-workflow.md +322 -0
- package/docs/research/gsd-study.md +69 -0
- package/docs/research/kimi-hooks-study.md +274 -0
- package/docs/research/mattpocock-skills-comparison.md +429 -0
- package/docs/research/mattpocock-skills-study.md +275 -0
- package/docs/research/oh-my-codex-study.md +279 -0
- package/docs/research/perplexity-agent-skills-design.md +168 -0
- package/docs/research/pmc-gstack-skill-study.md +122 -0
- package/docs/research/spec-kit-study.md +224 -0
- package/docs/research/superpowers-study.md +209 -0
- package/docs/roadmap/initial-roadmap.md +53 -0
- package/docs/solutions/README.md +45 -0
- package/docs/solutions/artifact-nesting-recovery.md +58 -0
- package/docs/solutions/session-context-restore-practice.md +67 -0
- package/docs/solutions/workflow/version-drift-recovery.md +49 -0
- package/docs/solutions/worktree-gate-recovery.md +62 -0
- package/docs/specs/README.md +28 -0
- package/docs/specs/claude.md +45 -0
- package/docs/specs/codex.md +44 -0
- package/docs/specs/cursor.md +44 -0
- package/hosts/adapters/claude.ts +29 -0
- package/hosts/adapters/codex.ts +27 -0
- package/hosts/adapters/cursor.ts +27 -0
- package/hosts/adapters/kimi.ts +27 -0
- package/hosts/claude.ts +23 -0
- package/hosts/codex.ts +26 -0
- package/hosts/cursor.ts +19 -0
- package/hosts/index.ts +33 -0
- package/hosts/registry.test.ts +52 -0
- package/hosts/registry.ts +57 -0
- package/hosts/schema.ts +58 -0
- package/package.json +52 -0
- package/scripts/browser.ts +185 -0
- package/scripts/cleanup.ts +142 -0
- package/scripts/commands/artifact.ts +115 -0
- package/scripts/commands/autopilot.ts +143 -0
- package/scripts/commands/capability.ts +57 -0
- package/scripts/commands/config.ts +69 -0
- package/scripts/commands/dag.ts +126 -0
- package/scripts/commands/feedback.ts +123 -0
- package/scripts/commands/gate.ts +291 -0
- package/scripts/commands/helpers.ts +126 -0
- package/scripts/commands/hook.ts +66 -0
- package/scripts/commands/init.ts +515 -0
- package/scripts/commands/issue.ts +825 -0
- package/scripts/commands/phase.ts +61 -0
- package/scripts/commands/preset.ts +159 -0
- package/scripts/commands/runtime.ts +199 -0
- package/scripts/commands/specify.ts +71 -0
- package/scripts/commands/upgrade.ts +243 -0
- package/scripts/commands/verify.ts +183 -0
- package/scripts/commands/wiki.ts +242 -0
- package/scripts/commands/workflow.ts +131 -0
- package/scripts/dev-skill.ts +55 -0
- package/scripts/discover-skills.ts +116 -0
- package/scripts/doctor.ts +410 -0
- package/scripts/dogfood-check.ts +125 -0
- package/scripts/eval-functional.ts +218 -0
- package/scripts/eval.ts +246 -0
- package/scripts/gen-skill-docs.ts +201 -0
- package/scripts/global-discover.ts +217 -0
- package/scripts/governance-check.ts +75 -0
- package/scripts/gxpm-check.ts +12 -0
- package/scripts/gxpm.ts +216 -0
- package/scripts/host-config.ts +62 -0
- package/scripts/install-claude-hooks.ts +138 -0
- package/scripts/install-codex-hooks.ts +271 -0
- package/scripts/install-hooks.ts +128 -0
- package/scripts/install-kimi-hooks.ts +92 -0
- package/scripts/install-skill.ts +184 -0
- package/scripts/phase-artifact-commands.ts +100 -0
- package/scripts/post-land-sync.ts +46 -0
- package/scripts/scaffold-check.ts +85 -0
- package/scripts/skill-naming-check.ts +78 -0
- package/scripts/skill-structure-check.ts +157 -0
- package/scripts/skills-lock-check.ts +60 -0
- package/scripts/sync-markdown-artifacts.ts +172 -0
- package/scripts/uninstall.ts +162 -0
- package/scripts/version.ts +47 -0
- package/scripts/wait-pr-ready.ts +407 -0
- package/skills/gxpm/SKILL.md +485 -0
- package/skills/gxpm/SKILL.md.tmpl +422 -0
- package/skills/gxpm/references/CANON.md +53 -0
- package/skills/gxpm/references/key-rules.md +130 -0
- package/skills/gxpm-architecture/SKILL.md +106 -0
- package/skills/gxpm-architecture/references/DEEPENING.md +37 -0
- package/skills/gxpm-architecture/references/INTERFACE-DESIGN.md +44 -0
- package/skills/gxpm-autopilot/SKILL.md +116 -0
- package/skills/gxpm-autopilot/SKILL.md.tmpl +107 -0
- package/skills/gxpm-browser/SKILL.md +105 -0
- package/skills/gxpm-browser/SKILL.md.tmpl +41 -0
- package/skills/gxpm-browser/references/commands.md +43 -0
- package/skills/gxpm-browser/references/evidence-path.md +20 -0
- package/skills/gxpm-build/SKILL.md +78 -0
- package/skills/gxpm-cleanup/SKILL.md +76 -0
- package/skills/gxpm-debug-issue/SKILL.md +39 -0
- package/skills/gxpm-diagnose/SKILL.md +220 -0
- package/skills/gxpm-diagnose/SKILL.md.tmpl +31 -0
- package/skills/gxpm-diagnose/references/feedback-loop.md +34 -0
- package/skills/gxpm-diagnose/references/feedback-loops.md +43 -0
- package/skills/gxpm-diagnose/references/phases.md +60 -0
- package/skills/gxpm-eval/SKILL.md +78 -0
- package/skills/gxpm-explore-codebase/SKILL.md +36 -0
- package/skills/gxpm-explore-codebase/scripts/summarize-communities.ts +51 -0
- package/skills/gxpm-feedback/SKILL.md +122 -0
- package/skills/gxpm-grill/SKILL.md +159 -0
- package/skills/gxpm-grill/SKILL.md.tmpl +77 -0
- package/skills/gxpm-grill/references/documentation-templates.md +56 -0
- package/skills/gxpm-grill/references/process.md +25 -0
- package/skills/gxpm-handoff/SKILL.md +112 -0
- package/skills/gxpm-hygiene/SKILL.md +69 -0
- package/skills/gxpm-implementer/SKILL.md +142 -0
- package/skills/gxpm-implementer/SKILL.md.tmpl +141 -0
- package/skills/gxpm-linear/SKILL.md +282 -0
- package/skills/gxpm-linear/SKILL.md.tmpl +86 -0
- package/skills/gxpm-linear/references/commands.md +75 -0
- package/skills/gxpm-linear/references/workflows.md +120 -0
- package/skills/gxpm-planning/SKILL.md +134 -0
- package/skills/gxpm-prototype/SKILL.md +64 -0
- package/skills/gxpm-refactor-safely/SKILL.md +62 -0
- package/skills/gxpm-review-army/SKILL.md +117 -0
- package/skills/gxpm-review-changes/SKILL.md +36 -0
- package/skills/gxpm-setup/SKILL.md +101 -0
- package/skills/gxpm-specifier/SKILL.md +135 -0
- package/skills/gxpm-tdd/SKILL.md +187 -0
- package/skills/gxpm-tdd/references/interface-design.md +23 -0
- package/skills/gxpm-tdd/references/mocking.md +27 -0
- package/skills/gxpm-tdd/references/red-green-refactor.md +61 -0
- package/skills/gxpm-tdd/references/troubleshooting.md +28 -0
- package/skills/gxpm-tdd/references/workflow.md +50 -0
- package/skills/gxpm-tdd/testing-anti-patterns.tmpl +304 -0
- package/skills/gxpm-triage/SKILL.md +160 -0
- package/skills/gxpm-verify/SKILL.md +107 -0
- package/skills/gxpm-write-skill/SKILL.md +131 -0
- package/skills/gxpm-zoom-out/SKILL.md +69 -0
- package/skills/maintain-hygiene-skills-lock/SKILL.md +54 -0
- package/skills/maintain-hygiene-skills-lock/SKILL.md.tmpl +53 -0
- package/templates/constitution-template.md +63 -0
- package/templates/hooks/gxpm-commit-msg +16 -0
- package/templates/hooks/gxpm-post-checkout +19 -0
- package/templates/hooks/gxpm-post-commit +7 -0
- package/templates/hooks/gxpm-post-merge +29 -0
- package/templates/hooks/gxpm-pre-commit +39 -0
- package/templates/hooks/gxpm-pre-push +33 -0
- package/templates/plan-template.md.tmpl +46 -0
- package/templates/spec-template.md.tmpl +63 -0
- package/templates/specify-stub.tmpl +22 -0
- 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
|