intentdocs 0.10.1 → 0.12.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/README.md +1 -1
- package/dist/{chunk-6X4BORMW.js → chunk-5GATVQEI.js} +1 -1
- package/dist/chunk-64I7RLP3.js +171 -0
- package/dist/{chunk-F52HPVNQ.js → chunk-BNEU6TLJ.js} +54 -20
- package/dist/chunk-GFKZ5CRL.js +12 -0
- package/dist/{chunk-34JCN66H.js → chunk-KTGQO3AE.js} +24 -1
- package/dist/{chunk-NHNBCNND.js → chunk-Q6BSSJK5.js} +37 -16
- package/dist/chunk-RGFGTIIH.js +536 -0
- package/dist/{chunk-PUGYRSMK.js → chunk-SDXDVNNP.js} +122 -65
- package/dist/{ci-XCOXFTLB.js → ci-CWPLSRCQ.js} +2 -2
- package/dist/{complexity-X3IJKNNV.js → complexity-HLJTXY55.js} +2 -2
- package/dist/{import-prd-IJTM6FZE.js → import-prd-FQI4J45M.js} +12 -5
- package/dist/index.js +347 -326
- package/dist/{keys-UVXCCWCS.js → keys-2W5V56FS.js} +30 -9
- package/dist/{log-XIH4BGFL.js → log-VTIAG4PC.js} +21 -5
- package/dist/{map-extract-V6D27I3H.js → map-extract-4C44YGGD.js} +1 -1
- package/dist/{map-import-LORTI4K4.js → map-import-TSSDU766.js} +5 -4
- package/dist/pull-MDI63WI7.js +476 -0
- package/dist/push-7LJ654MJ.js +220 -0
- package/dist/{research-CO3W7PLC.js → research-L27DLYXC.js} +2 -2
- package/dist/scope-ORIFXV4P.js +13 -0
- package/dist/{setup-WSYIG2CR.js → setup-W4PKG6GB.js} +5 -5
- package/package.json +6 -5
- package/dist/chunk-JX2RLGOM.js +0 -59
- package/dist/scope-ZZLKQEF5.js +0 -11
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
import {
|
|
2
|
+
login
|
|
3
|
+
} from "./chunk-64I7RLP3.js";
|
|
4
|
+
import {
|
|
5
|
+
checkoutMismatch
|
|
6
|
+
} from "./chunk-KTGQO3AE.js";
|
|
7
|
+
import {
|
|
8
|
+
checkoutConfig,
|
|
9
|
+
readConfig,
|
|
10
|
+
readIntentFolder,
|
|
11
|
+
readSyncBase,
|
|
12
|
+
specOfPushStory,
|
|
13
|
+
writeConfig,
|
|
14
|
+
writeSyncBase
|
|
15
|
+
} from "./chunk-RGFGTIIH.js";
|
|
16
|
+
import {
|
|
17
|
+
choose,
|
|
18
|
+
closePrompt,
|
|
19
|
+
isInteractive
|
|
20
|
+
} from "./chunk-WRRUCCVZ.js";
|
|
21
|
+
import {
|
|
22
|
+
authenticatedFetch,
|
|
23
|
+
getAuth
|
|
24
|
+
} from "./chunk-XZE2XP6O.js";
|
|
25
|
+
import "./chunk-UYTVTYNB.js";
|
|
26
|
+
|
|
27
|
+
// src/commands/push.ts
|
|
28
|
+
import { exec } from "child_process";
|
|
29
|
+
|
|
30
|
+
// src/lib/push-api.ts
|
|
31
|
+
var PUSH_ENDPOINTS = {
|
|
32
|
+
listWorkspaces: "/api/cli/workspaces",
|
|
33
|
+
createPersonalWorkspace: "/api/cli/workspaces/personal",
|
|
34
|
+
createProject: "/api/cli/push",
|
|
35
|
+
updateProject: (projectId) => `/api/cli/push/${encodeURIComponent(projectId)}`,
|
|
36
|
+
projectMaps: (projectId) => `/api/cli/projects/${encodeURIComponent(projectId)}/maps`
|
|
37
|
+
};
|
|
38
|
+
var PushApiError = class extends Error {
|
|
39
|
+
constructor(message, status, code = null, problems = [], projectId = null) {
|
|
40
|
+
super(message);
|
|
41
|
+
this.status = status;
|
|
42
|
+
this.code = code;
|
|
43
|
+
this.problems = problems;
|
|
44
|
+
this.projectId = projectId;
|
|
45
|
+
this.name = "PushApiError";
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
async function call(fetcher, path, init, what) {
|
|
49
|
+
const res = await fetcher(path, init);
|
|
50
|
+
if (!res.ok) {
|
|
51
|
+
const body = await res.json().catch(() => ({}));
|
|
52
|
+
const nested = typeof body.error === "object" && body.error ? body.error : null;
|
|
53
|
+
const said = body.message ?? (nested ? nested.hint ?? nested.message : void 0);
|
|
54
|
+
throw new PushApiError(
|
|
55
|
+
said ?? `Couldn't ${what} (the server returned ${res.status}).`,
|
|
56
|
+
res.status,
|
|
57
|
+
typeof body.error === "string" ? body.error : null,
|
|
58
|
+
Array.isArray(body.problems) ? body.problems : [],
|
|
59
|
+
body.project_id ?? null
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
return await res.json();
|
|
63
|
+
}
|
|
64
|
+
var json = (method, body) => body === void 0 ? { method } : { method, headers: { "Content-Type": "application/json" }, body: JSON.stringify(body) };
|
|
65
|
+
function pushApi(fetcher = authenticatedFetch) {
|
|
66
|
+
return {
|
|
67
|
+
listWorkspaces: () => call(fetcher, PUSH_ENDPOINTS.listWorkspaces, json("GET"), "list your workspaces").then((r) => r.workspaces),
|
|
68
|
+
createPersonalWorkspace: () => call(fetcher, PUSH_ENDPOINTS.createPersonalWorkspace, json("POST"), "open your personal workspace").then(
|
|
69
|
+
(r) => r.workspace
|
|
70
|
+
),
|
|
71
|
+
createProject: (workspaceId, payload) => call(fetcher, PUSH_ENDPOINTS.createProject, json("POST", { workspace_id: workspaceId, intent: payload }), "create the project"),
|
|
72
|
+
updateProject: (projectId, mapId, payload) => call(fetcher, PUSH_ENDPOINTS.updateProject(projectId), json("PUT", { map_id: mapId, intent: payload }), "update the project"),
|
|
73
|
+
/** The project's story maps, newest first. */
|
|
74
|
+
projectMaps: (projectId) => call(fetcher, PUSH_ENDPOINTS.projectMaps(projectId), json("GET"), "find the project's story map").then(
|
|
75
|
+
(r) => r.maps
|
|
76
|
+
)
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
// src/commands/push.ts
|
|
81
|
+
var PushError = class extends Error {
|
|
82
|
+
constructor(message, details = []) {
|
|
83
|
+
super(message);
|
|
84
|
+
this.details = details;
|
|
85
|
+
this.name = "PushError";
|
|
86
|
+
}
|
|
87
|
+
};
|
|
88
|
+
function summary(p) {
|
|
89
|
+
const criteria = p.stories.reduce((n, s) => n + s.criteria.length, 0);
|
|
90
|
+
return `${p.stories.length} stories, ${criteria} criteria, ${p.releases.length} releases, ${p.plans.length} plans, ${p.decisions.length} decisions`;
|
|
91
|
+
}
|
|
92
|
+
async function pickWorkspace(deps, auth) {
|
|
93
|
+
const workspaces = await deps.api.listWorkspaces();
|
|
94
|
+
if (auth.binding) {
|
|
95
|
+
const { orgId, workspaceName } = auth.binding;
|
|
96
|
+
return workspaces.find((w) => w.orgId === orgId) ?? { id: orgId, orgId, name: workspaceName ?? null, personal: false };
|
|
97
|
+
}
|
|
98
|
+
if (workspaces.length === 0) return deps.api.createPersonalWorkspace();
|
|
99
|
+
if (workspaces.length === 1) return workspaces[0];
|
|
100
|
+
const personal = Math.max(0, workspaces.findIndex((w) => w.personal));
|
|
101
|
+
return workspaces[await deps.chooseWorkspace(workspaces, personal)] ?? workspaces[personal];
|
|
102
|
+
}
|
|
103
|
+
function pushNotes(result) {
|
|
104
|
+
const notes = [];
|
|
105
|
+
const statuses = result.ignored?.statuses ?? 0;
|
|
106
|
+
if (statuses > 0) {
|
|
107
|
+
notes.push(
|
|
108
|
+
`${statuses} story ${statuses === 1 ? "status was" : "statuses were"} not applied: intentdocs sets status itself. New stories start as To do, existing ones keep theirs, and they move through build and acceptance in intentdocs.`
|
|
109
|
+
);
|
|
110
|
+
}
|
|
111
|
+
const missing = result.summary?.stories?.missing ?? [];
|
|
112
|
+
if (missing.length) notes.push(`In intentdocs but not in intent/: ${missing.join(", ")}. Push never deletes, so they stay.`);
|
|
113
|
+
if (result.not_stored?.length) notes.push(`Not stored by intentdocs yet: ${result.not_stored.join(", ")}. They stay in intent/.`);
|
|
114
|
+
return notes;
|
|
115
|
+
}
|
|
116
|
+
async function runPush(deps) {
|
|
117
|
+
const read = readIntentFolder(deps.cwd);
|
|
118
|
+
if (!read.ok) throw new PushError(`intent/ can't be pushed yet (${read.dir}):`, read.errors);
|
|
119
|
+
const payload = read.payload;
|
|
120
|
+
let auth = deps.getAuth();
|
|
121
|
+
if (!auth) {
|
|
122
|
+
deps.log("You need to log in first. Opening the browser\u2026");
|
|
123
|
+
await deps.login();
|
|
124
|
+
auth = deps.getAuth();
|
|
125
|
+
if (!auth) throw new PushError("Login didn't complete, so nothing was pushed.");
|
|
126
|
+
}
|
|
127
|
+
const existing = deps.readConfig();
|
|
128
|
+
const linked = existing?.projectId && existing.mapId ? { projectId: existing.projectId, mapId: existing.mapId } : null;
|
|
129
|
+
if (existing) {
|
|
130
|
+
const mismatch = checkoutMismatch(existing, auth.binding);
|
|
131
|
+
if (mismatch) throw new PushError(mismatch);
|
|
132
|
+
}
|
|
133
|
+
deps.log(`Pushing "${payload.project.name}": ${summary(payload)}`);
|
|
134
|
+
let workspace = null;
|
|
135
|
+
let result;
|
|
136
|
+
let created = !linked;
|
|
137
|
+
if (linked) {
|
|
138
|
+
result = await deps.api.updateProject(linked.projectId, linked.mapId, payload);
|
|
139
|
+
} else {
|
|
140
|
+
workspace = await pickWorkspace(deps, auth);
|
|
141
|
+
try {
|
|
142
|
+
result = await deps.api.createProject(workspace.id, payload);
|
|
143
|
+
} catch (err) {
|
|
144
|
+
if (!(err instanceof PushApiError && err.code === "ALREADY_PUSHED" && err.projectId)) throw err;
|
|
145
|
+
const [map] = await deps.api.projectMaps(err.projectId);
|
|
146
|
+
if (!map) throw err;
|
|
147
|
+
deps.log(`"${payload.project.name}" was pushed before, so this updates that project.`);
|
|
148
|
+
result = await deps.api.updateProject(err.projectId, map.id, payload);
|
|
149
|
+
created = false;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
deps.writeConfig({
|
|
153
|
+
...existing,
|
|
154
|
+
...checkoutConfig({ apiBase: auth.apiBase, project: result.project, map: result.map, appUrl: existing?.appUrl }),
|
|
155
|
+
lastPushed: deps.now().toISOString()
|
|
156
|
+
});
|
|
157
|
+
const base = deps.readSyncBase();
|
|
158
|
+
for (const story of payload.stories) {
|
|
159
|
+
base.stories[story.key] = specOfPushStory(story);
|
|
160
|
+
base.activities[story.activity_id] = story.activity;
|
|
161
|
+
}
|
|
162
|
+
for (const release of payload.releases) base.releases[String(release.number)] = release.title;
|
|
163
|
+
for (const key of result.summary?.stories?.created ?? []) base.statuses[key] = "todo";
|
|
164
|
+
deps.writeSyncBase(base);
|
|
165
|
+
deps.openBrowser(result.url);
|
|
166
|
+
return { result, created, workspace };
|
|
167
|
+
}
|
|
168
|
+
function openBrowser(url) {
|
|
169
|
+
const cmd = process.platform === "darwin" ? `open "${url}"` : process.platform === "win32" ? `start "" "${url}"` : `xdg-open "${url}"`;
|
|
170
|
+
exec(cmd);
|
|
171
|
+
}
|
|
172
|
+
async function push() {
|
|
173
|
+
const deps = {
|
|
174
|
+
cwd: process.cwd(),
|
|
175
|
+
api: pushApi(),
|
|
176
|
+
getAuth,
|
|
177
|
+
login,
|
|
178
|
+
readConfig,
|
|
179
|
+
writeConfig,
|
|
180
|
+
readSyncBase,
|
|
181
|
+
writeSyncBase,
|
|
182
|
+
openBrowser,
|
|
183
|
+
chooseWorkspace: async (workspaces, defaultIndex) => {
|
|
184
|
+
if (!isInteractive()) return defaultIndex;
|
|
185
|
+
const order = [defaultIndex, ...workspaces.map((_, i) => i).filter((i) => i !== defaultIndex)];
|
|
186
|
+
const labels = order.map((i, n) => `${workspaces[i].personal ? "Personal" : workspaces[i].name ?? "Workspace"}${n === 0 ? " (default)" : ""}`);
|
|
187
|
+
return order[await choose("Which workspace should this project go in?", labels)];
|
|
188
|
+
},
|
|
189
|
+
log: (line) => console.log(line),
|
|
190
|
+
now: () => /* @__PURE__ */ new Date()
|
|
191
|
+
};
|
|
192
|
+
try {
|
|
193
|
+
const { result, created } = await runPush(deps);
|
|
194
|
+
console.log(`
|
|
195
|
+
\u2713 ${created ? "Created" : "Updated"} "${result.map.title}"${result.project.workspace ? ` in ${result.project.workspace.name ?? "your workspace"}` : ""}`);
|
|
196
|
+
for (const note of pushNotes(result)) console.log(` ${note}`);
|
|
197
|
+
console.log(` Linked this repo in .intent/config.json \u2014 run \`npx intentdocs push\` again to sync.`);
|
|
198
|
+
console.log(` Opening ${result.url}
|
|
199
|
+
`);
|
|
200
|
+
} catch (err) {
|
|
201
|
+
if (err instanceof PushError) {
|
|
202
|
+
console.error(`\u2717 ${err.message}`);
|
|
203
|
+
for (const d of err.details) console.error(` - ${d}`);
|
|
204
|
+
} else if (err instanceof PushApiError) {
|
|
205
|
+
console.error(`\u2717 ${err.message}`);
|
|
206
|
+
for (const p of err.problems) console.error(` - ${p}`);
|
|
207
|
+
} else {
|
|
208
|
+
console.error(`\u2717 Push failed: ${err.message}`);
|
|
209
|
+
}
|
|
210
|
+
process.exitCode = 1;
|
|
211
|
+
} finally {
|
|
212
|
+
closePrompt();
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
export {
|
|
216
|
+
PushError,
|
|
217
|
+
push,
|
|
218
|
+
pushNotes,
|
|
219
|
+
runPush
|
|
220
|
+
};
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import {
|
|
2
|
+
connectCheckout,
|
|
3
|
+
scope
|
|
4
|
+
} from "./chunk-Q6BSSJK5.js";
|
|
5
|
+
import "./chunk-KTGQO3AE.js";
|
|
6
|
+
import "./chunk-RGFGTIIH.js";
|
|
7
|
+
import "./chunk-WRRUCCVZ.js";
|
|
8
|
+
import "./chunk-XZE2XP6O.js";
|
|
9
|
+
import "./chunk-UYTVTYNB.js";
|
|
10
|
+
export {
|
|
11
|
+
connectCheckout,
|
|
12
|
+
scope
|
|
13
|
+
};
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
setup
|
|
3
|
-
} from "./chunk-
|
|
4
|
-
import "./chunk-
|
|
5
|
-
import "./chunk-F52HPVNQ.js";
|
|
3
|
+
} from "./chunk-SDXDVNNP.js";
|
|
4
|
+
import "./chunk-BNEU6TLJ.js";
|
|
6
5
|
import "./chunk-LQTPZL2Y.js";
|
|
7
|
-
import "./chunk-
|
|
8
|
-
import "./chunk-
|
|
6
|
+
import "./chunk-KPQ42YE6.js";
|
|
7
|
+
import "./chunk-KTGQO3AE.js";
|
|
8
|
+
import "./chunk-RGFGTIIH.js";
|
|
9
9
|
import "./chunk-WRRUCCVZ.js";
|
|
10
10
|
import "./chunk-XZE2XP6O.js";
|
|
11
11
|
import "./chunk-UYTVTYNB.js";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "intentdocs",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.0",
|
|
4
4
|
"description": "CLI for intentdocs.com — intent-driven development: connect your codebase to your product specs",
|
|
5
5
|
"bin": {
|
|
6
6
|
"intentdocs": "bin/intentdocs.js",
|
|
@@ -30,16 +30,17 @@
|
|
|
30
30
|
"author": "intentdocs.com",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
|
-
"@modelcontextprotocol/sdk": "^1.
|
|
34
|
-
"commander": "^12.0.0",
|
|
33
|
+
"@modelcontextprotocol/sdk": "^1.32.1",
|
|
35
34
|
"chalk": "^5.3.0",
|
|
35
|
+
"commander": "^12.0.0",
|
|
36
36
|
"ora": "^8.0.0",
|
|
37
|
-
"posthog-node": "^4.0.0"
|
|
37
|
+
"posthog-node": "^4.0.0",
|
|
38
|
+
"yaml": "^2.9.1"
|
|
38
39
|
},
|
|
39
40
|
"devDependencies": {
|
|
41
|
+
"@types/node": "^20.0.0",
|
|
40
42
|
"tsup": "^8.0.0",
|
|
41
43
|
"typescript": "^5.4.0",
|
|
42
|
-
"@types/node": "^20.0.0",
|
|
43
44
|
"vitest": "^4.1.2"
|
|
44
45
|
}
|
|
45
46
|
}
|
package/dist/chunk-JX2RLGOM.js
DELETED
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
canonicalApiBase
|
|
3
|
-
} from "./chunk-UYTVTYNB.js";
|
|
4
|
-
|
|
5
|
-
// src/lib/config.ts
|
|
6
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync } from "fs";
|
|
7
|
-
import { join, dirname } from "path";
|
|
8
|
-
var CONFIG_DIR = ".intent";
|
|
9
|
-
var CONFIG_FILE = "config.json";
|
|
10
|
-
function configPath() {
|
|
11
|
-
let dir = process.cwd();
|
|
12
|
-
while (true) {
|
|
13
|
-
const candidate = join(dir, CONFIG_DIR, CONFIG_FILE);
|
|
14
|
-
if (existsSync(candidate)) return candidate;
|
|
15
|
-
const parent = dirname(dir);
|
|
16
|
-
if (parent === dir) break;
|
|
17
|
-
dir = parent;
|
|
18
|
-
}
|
|
19
|
-
return join(process.cwd(), CONFIG_DIR, CONFIG_FILE);
|
|
20
|
-
}
|
|
21
|
-
function hasConfig() {
|
|
22
|
-
return existsSync(configPath());
|
|
23
|
-
}
|
|
24
|
-
function readConfig() {
|
|
25
|
-
try {
|
|
26
|
-
const raw = readFileSync(configPath(), "utf-8");
|
|
27
|
-
const config = JSON.parse(raw);
|
|
28
|
-
return config.apiBase ? { ...config, apiBase: canonicalApiBase(config.apiBase) } : config;
|
|
29
|
-
} catch {
|
|
30
|
-
return null;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
function writeConfig(config) {
|
|
34
|
-
const existing = existsSync(configPath()) ? configPath() : null;
|
|
35
|
-
const dir = existing ? dirname(existing) : join(process.cwd(), CONFIG_DIR);
|
|
36
|
-
if (!existsSync(dir)) mkdirSync(dir, { recursive: true });
|
|
37
|
-
writeFileSync(join(dir, CONFIG_FILE), JSON.stringify(config, null, 2) + "\n");
|
|
38
|
-
}
|
|
39
|
-
function requireConfig() {
|
|
40
|
-
const config = readConfig();
|
|
41
|
-
if (!config) {
|
|
42
|
-
console.error("Not initialized. Run: npx intentdocs init");
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
if (!config.mapId) {
|
|
46
|
-
console.error(
|
|
47
|
-
"This checkout was connected with a sync token, which has been retired: every sync now runs as you.\nRemove .intent/config.json and run `npx intentdocs login` then `npx intentdocs init` to reconnect."
|
|
48
|
-
);
|
|
49
|
-
process.exit(1);
|
|
50
|
-
}
|
|
51
|
-
return config;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export {
|
|
55
|
-
hasConfig,
|
|
56
|
-
readConfig,
|
|
57
|
-
writeConfig,
|
|
58
|
-
requireConfig
|
|
59
|
-
};
|
package/dist/scope-ZZLKQEF5.js
DELETED