@pathmode/cli 2.0.0 → 2.0.1
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/README.md +189 -25
- package/dist/index.js +23 -7
- package/package.json +4 -1
package/README.md
CHANGED
|
@@ -1,38 +1,202 @@
|
|
|
1
|
-
#
|
|
1
|
+
# @pathmode/cli
|
|
2
2
|
|
|
3
|
-
The
|
|
3
|
+
The command-line interface for [Pathmode](https://pathmode.io). It syncs your repo's agent context files with your Pathmode workspace, pushes implementation status back, and validates specs against the [IntentSpec](https://intentspec.org/spec) standard.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
## Install
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
```bash
|
|
8
|
+
npm i -g @pathmode/cli
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
This installs the `pathmode` binary. Check it with:
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
pathmode --version
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
You can also run it without installing:
|
|
18
|
+
|
|
19
|
+
```bash
|
|
20
|
+
npx @pathmode/cli validate
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Quick start
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
npm i -g @pathmode/cli
|
|
27
|
+
pathmode login # paste your pm_live_ API key
|
|
28
|
+
pathmode init # write intent.md, .cursorrules, CLAUDE.md
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
`validate` works with no account. The other five commands talk to the Pathmode API and need a key.
|
|
32
|
+
|
|
33
|
+
## Authentication
|
|
34
|
+
|
|
35
|
+
1. Open your Pathmode workspace, go to **Workspace Settings > API Keys**, and create a key. Keys start with `pm_live_`.
|
|
36
|
+
2. Run `pathmode login`. With no flags it prompts for the key and workspace ID, verifies them against the API, and reports the workspace it connected to.
|
|
37
|
+
|
|
38
|
+
```bash
|
|
39
|
+
pathmode login
|
|
40
|
+
# or non-interactively:
|
|
41
|
+
pathmode login --key pm_live_xxx --workspace <workspace-id>
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Credentials are written to `~/.pathmode/config.json` with `0600` permissions.
|
|
45
|
+
|
|
46
|
+
### Environment variables
|
|
47
|
+
|
|
48
|
+
Environment variables take precedence over the config file, so use them in CI instead of committing a config:
|
|
49
|
+
|
|
50
|
+
| Variable | Purpose | Default |
|
|
51
|
+
|---|---|---|
|
|
52
|
+
| `PATHMODE_API_KEY` | API key (`pm_live_...`) | none |
|
|
53
|
+
| `PATHMODE_API_URL` | API base URL | `https://pathmode.io` |
|
|
54
|
+
| `PATHMODE_WORKSPACE_ID` | Workspace ID | none |
|
|
55
|
+
|
|
56
|
+
## Commands
|
|
57
|
+
|
|
58
|
+
| Command | What it does | Needs auth |
|
|
59
|
+
|---|---|---|
|
|
60
|
+
| [`validate`](#validate-file) | Check a spec against the IntentSpec schema | No |
|
|
61
|
+
| [`login`](#login) | Store and verify your API key | Sets it up |
|
|
62
|
+
| [`init`](#init) | Scaffold intent files in a fresh repo | Yes |
|
|
63
|
+
| [`pull`](#pull) | Refresh local context files from the workspace | Yes |
|
|
64
|
+
| [`push`](#push) | Send status changes and notes back | Yes |
|
|
65
|
+
| [`status`](#status) | List workspace intents and their status | Yes |
|
|
66
|
+
|
|
67
|
+
### `validate [file]`
|
|
68
|
+
|
|
69
|
+
Validates the YAML frontmatter of a markdown file against the [IntentSpec schema](https://intentspec.org/schema.json). Exits `0` when valid and `1` when not, printing each failed field, so it works as a CI gate.
|
|
70
|
+
|
|
71
|
+
```bash
|
|
72
|
+
pathmode validate # defaults to intent.md
|
|
73
|
+
pathmode validate specs/login.md
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Required fields are `id`, `status`, `objective`, and `outcomes`. `status` must be one of `draft`, `validated`, `approved`, `shipped`, or `verified`. See the [full standard](https://intentspec.org/spec) for the optional fields.
|
|
77
|
+
|
|
78
|
+
### `login`
|
|
79
|
+
|
|
80
|
+
| Option | Description | Default |
|
|
81
|
+
|---|---|---|
|
|
82
|
+
| `--key <key>` | API key, starting with `pm_live_` | prompts |
|
|
83
|
+
| `--workspace <id>` | Workspace ID | prompts |
|
|
84
|
+
| `--url <url>` | Pathmode API URL | `https://pathmode.io` |
|
|
85
|
+
|
|
86
|
+
If the connection test fails the config is still saved, so you can fix the network and retry with `pathmode pull`.
|
|
87
|
+
|
|
88
|
+
### `init`
|
|
89
|
+
|
|
90
|
+
Pulls your workspace context and writes three files into the current directory:
|
|
91
|
+
|
|
92
|
+
- `intent.md`, the current intent spec
|
|
93
|
+
- `.cursorrules`, rules for Cursor
|
|
94
|
+
- `CLAUDE.md`, workspace context for Claude Code
|
|
8
95
|
|
|
9
|
-
|
|
96
|
+
```bash
|
|
97
|
+
pathmode init
|
|
98
|
+
pathmode init --dry-run # preview without writing
|
|
99
|
+
pathmode init --intent abc-123 # use a specific intent
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
| Option | Description |
|
|
103
|
+
|---|---|
|
|
104
|
+
| `--intent <id>` | Use a specific intent instead of the current approved one |
|
|
105
|
+
| `--dry-run` | Print what would be written and exit |
|
|
106
|
+
|
|
107
|
+
### `pull`
|
|
108
|
+
|
|
109
|
+
Re-syncs the files `init` created. Run it whenever the spec changes upstream.
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
pathmode pull
|
|
113
|
+
pathmode pull --only claude-md,intent-md
|
|
114
|
+
pathmode pull --only agents-md
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
| Option | Description |
|
|
118
|
+
|---|---|
|
|
119
|
+
| `--intent <id>` | Pull a specific intent |
|
|
120
|
+
| `--only <files>` | Comma-separated subset of `claude-md`, `agents-md`, `cursorrules`, `intent-md` |
|
|
121
|
+
|
|
122
|
+
`AGENTS.md` is opt-in. It carries the same context as `CLAUDE.md`, so syncing it by default would only duplicate that file. Request it with `--only agents-md` if you use OpenAI Codex or a recent Cursor version.
|
|
123
|
+
|
|
124
|
+
### `push`
|
|
125
|
+
|
|
126
|
+
Reports progress back to the workspace. The intent ID comes from the `id` in your local `intent.md` frontmatter unless you pass `--intent`.
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
pathmode push --shipped
|
|
130
|
+
pathmode push --verified
|
|
131
|
+
pathmode push --note "Rate limiting added in middleware, not the route handler"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
| Option | Description |
|
|
135
|
+
|---|---|
|
|
136
|
+
| `--shipped` | Mark the intent as shipped |
|
|
137
|
+
| `--verified` | Mark the intent as verified |
|
|
138
|
+
| `--note <text>` | Log an implementation note against the intent |
|
|
139
|
+
| `--intent <id>` | Target a specific intent |
|
|
140
|
+
|
|
141
|
+
### `status`
|
|
142
|
+
|
|
143
|
+
Lists the workspace's intents with their status, severity, and stage.
|
|
144
|
+
|
|
145
|
+
```bash
|
|
146
|
+
pathmode status
|
|
147
|
+
pathmode status --filter approved
|
|
148
|
+
```
|
|
149
|
+
|
|
150
|
+
| Option | Description |
|
|
151
|
+
|---|---|
|
|
152
|
+
| `--filter <status>` | One of `draft`, `validated`, `approved`, `shipped`, `verified` |
|
|
153
|
+
|
|
154
|
+
## How CLAUDE.md is updated
|
|
155
|
+
|
|
156
|
+
`init` and `pull` do not overwrite `CLAUDE.md` or `AGENTS.md`. They replace only the block between these markers, leaving your own notes and conventions intact:
|
|
157
|
+
|
|
158
|
+
```markdown
|
|
159
|
+
<!-- PATHMODE:START - Do not edit this section manually -->
|
|
160
|
+
... generated content ...
|
|
161
|
+
<!-- PATHMODE:END -->
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
If the markers are absent the section is appended. `.cursorrules` and `intent.md` are fully replaced on every sync.
|
|
165
|
+
|
|
166
|
+
## Using it in CI
|
|
167
|
+
|
|
168
|
+
Validate every spec on a pull request:
|
|
169
|
+
|
|
170
|
+
```yaml
|
|
171
|
+
- uses: actions/checkout@v4
|
|
172
|
+
- run: npx @pathmode/cli validate intent.md
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
Keep context files fresh on a schedule, using repository secrets for the credentials:
|
|
10
176
|
|
|
11
177
|
```yaml
|
|
12
|
-
|
|
178
|
+
- run: npm i -g @pathmode/cli
|
|
179
|
+
- run: pathmode pull
|
|
180
|
+
env:
|
|
181
|
+
PATHMODE_API_KEY: ${{ secrets.PATHMODE_API_KEY }}
|
|
182
|
+
PATHMODE_WORKSPACE_ID: ${{ secrets.PATHMODE_WORKSPACE_ID }}
|
|
183
|
+
```
|
|
13
184
|
|
|
14
|
-
|
|
185
|
+
This package also ships an `action.yml`, so the same bundle runs directly as a GitHub Action that validates a spec. It reads the `file` input, defaulting to `intent.md`.
|
|
15
186
|
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
- uses: actions/checkout@v4
|
|
21
|
-
|
|
22
|
-
- name: Validate Intent Spec
|
|
23
|
-
uses: JanneL/validate-intentspec-action@v1
|
|
24
|
-
with:
|
|
25
|
-
file: 'intent.md' # Optional (default: intent.md)
|
|
187
|
+
```yaml
|
|
188
|
+
- uses: JanneL/validate-intentspec-action@v1
|
|
189
|
+
with:
|
|
190
|
+
file: 'intent.md'
|
|
26
191
|
```
|
|
27
192
|
|
|
28
|
-
##
|
|
193
|
+
## IntentSpec
|
|
194
|
+
|
|
195
|
+
`validate` checks against IntentSpec, the open standard for describing what a change is supposed to accomplish before an agent writes the code.
|
|
29
196
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
| `file` | Path to the intent markdown file | `intent.md` | No |
|
|
197
|
+
- Standard: <https://intentspec.org/spec>
|
|
198
|
+
- JSON Schema: <https://intentspec.org/schema.json>
|
|
33
199
|
|
|
34
|
-
##
|
|
200
|
+
## License
|
|
35
201
|
|
|
36
|
-
|
|
37
|
-
2. **Enforce Structure:** Validate that `objective`, `outcomes`, and `constraints` are defined.
|
|
38
|
-
3. **Governance:** Make IntentSpecs a required part of your PR process.
|
|
202
|
+
MIT
|
package/dist/index.js
CHANGED
|
@@ -11150,6 +11150,11 @@ class PathmodeClient {
|
|
|
11150
11150
|
const res = await this.fetch('/export?format=claude-md');
|
|
11151
11151
|
return res.text();
|
|
11152
11152
|
}
|
|
11153
|
+
/** Export AGENTS.md content (same workspace context as CLAUDE.md, for Codex / modern Cursor) */
|
|
11154
|
+
async exportAgentsMd() {
|
|
11155
|
+
const res = await this.fetch('/export?format=agents-md');
|
|
11156
|
+
return res.text();
|
|
11157
|
+
}
|
|
11153
11158
|
/** Export .cursorrules content */
|
|
11154
11159
|
async exportCursorRules(intentId) {
|
|
11155
11160
|
const params = intentId ? `?format=cursorrules&intent_id=${intentId}` : '?format=cursorrules';
|
|
@@ -11207,7 +11212,7 @@ const isGitHubAction = !!process.env.GITHUB_ACTIONS || !!process.env.INPUT_FILE;
|
|
|
11207
11212
|
program
|
|
11208
11213
|
.name('pathmode')
|
|
11209
11214
|
.description('Pathmode CLI — Intent Engineering for AI product teams')
|
|
11210
|
-
.version('2.0.
|
|
11215
|
+
.version('2.0.1');
|
|
11211
11216
|
// ============================================================
|
|
11212
11217
|
// validate — Existing command (preserved)
|
|
11213
11218
|
// ============================================================
|
|
@@ -11353,7 +11358,7 @@ program
|
|
|
11353
11358
|
console.log(chalk_1.default.green(`\u2705 Written: ${cursorPath}`));
|
|
11354
11359
|
// Write or update CLAUDE.md
|
|
11355
11360
|
const claudePath = path_1.default.join(cwd, 'CLAUDE.md');
|
|
11356
|
-
|
|
11361
|
+
writePathmodeSection(claudePath, claudeMd);
|
|
11357
11362
|
console.log(chalk_1.default.green(`\u2705 Written: ${claudePath}`));
|
|
11358
11363
|
console.log(chalk_1.default.cyan('\n\u2728 Project initialized with Pathmode intent context!'));
|
|
11359
11364
|
console.log(chalk_1.default.gray('Run `pathmode pull` to update these files later.'));
|
|
@@ -11370,7 +11375,7 @@ program
|
|
|
11370
11375
|
.command('pull')
|
|
11371
11376
|
.description('Pull latest intent data from Pathmode and update local files')
|
|
11372
11377
|
.option('--intent <id>', 'Specific intent ID to pull')
|
|
11373
|
-
.option('--only <files>', 'Only update specific files (claude-md,cursorrules,intent-md)', '')
|
|
11378
|
+
.option('--only <files>', 'Only update specific files (claude-md,agents-md,cursorrules,intent-md)', '')
|
|
11374
11379
|
.action(async (options) => {
|
|
11375
11380
|
console.log(chalk_1.default.cyan('\n\u2b07\ufe0f Pathmode Pull\n'));
|
|
11376
11381
|
const config = (0, api_client_1.loadConfig)();
|
|
@@ -11387,9 +11392,20 @@ program
|
|
|
11387
11392
|
console.log(chalk_1.default.gray('Pulling CLAUDE.md content...'));
|
|
11388
11393
|
const claudeMd = await client.exportClaudeMd();
|
|
11389
11394
|
const claudePath = path_1.default.join(cwd, 'CLAUDE.md');
|
|
11390
|
-
|
|
11395
|
+
writePathmodeSection(claudePath, claudeMd);
|
|
11391
11396
|
console.log(chalk_1.default.green(`\u2705 Updated: CLAUDE.md`));
|
|
11392
11397
|
}
|
|
11398
|
+
// AGENTS.md is opt-in (Codex / modern Cursor). It carries the same
|
|
11399
|
+
// agent-agnostic context as CLAUDE.md, so syncing it by default would
|
|
11400
|
+
// just duplicate CLAUDE.md \u2014 only write it when explicitly requested
|
|
11401
|
+
// via --only agents-md.
|
|
11402
|
+
if (onlyFiles.includes('agents-md')) {
|
|
11403
|
+
console.log(chalk_1.default.gray('Pulling AGENTS.md content...'));
|
|
11404
|
+
const agentsMd = await client.exportAgentsMd();
|
|
11405
|
+
const agentsPath = path_1.default.join(cwd, 'AGENTS.md');
|
|
11406
|
+
writePathmodeSection(agentsPath, agentsMd);
|
|
11407
|
+
console.log(chalk_1.default.green(`\u2705 Updated: AGENTS.md`));
|
|
11408
|
+
}
|
|
11393
11409
|
if (shouldUpdate('cursorrules')) {
|
|
11394
11410
|
console.log(chalk_1.default.gray('Pulling .cursorrules...'));
|
|
11395
11411
|
const cursorRules = await client.exportCursorRules(options.intent);
|
|
@@ -11511,10 +11527,10 @@ program
|
|
|
11511
11527
|
const PATHMODE_START = '<!-- PATHMODE:START - Do not edit this section manually -->';
|
|
11512
11528
|
const PATHMODE_END = '<!-- PATHMODE:END -->';
|
|
11513
11529
|
/**
|
|
11514
|
-
* Write Pathmode section into CLAUDE.md
|
|
11515
|
-
* outside the PATHMODE:START/END markers.
|
|
11530
|
+
* Write the Pathmode section into a CLAUDE.md / AGENTS.md style file,
|
|
11531
|
+
* preserving any existing content outside the PATHMODE:START/END markers.
|
|
11516
11532
|
*/
|
|
11517
|
-
function
|
|
11533
|
+
function writePathmodeSection(filePath, pathmodeContent) {
|
|
11518
11534
|
if (fs_1.default.existsSync(filePath)) {
|
|
11519
11535
|
const existing = fs_1.default.readFileSync(filePath, 'utf-8');
|
|
11520
11536
|
const startIdx = existing.indexOf(PATHMODE_START);
|
package/package.json
CHANGED