@agentteams/cli 0.1.56-dev.218 → 0.1.56-dev.221

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 (120) hide show
  1. package/.prettierignore +4 -0
  2. package/.prettierrc.json +6 -0
  3. package/README.md +7 -7
  4. package/dist/api/coaction.d.ts.map +1 -1
  5. package/dist/api/coaction.js +3 -9
  6. package/dist/api/coaction.js.map +1 -1
  7. package/dist/api/codeReview.d.ts.map +1 -1
  8. package/dist/api/codeReview.js +1 -3
  9. package/dist/api/codeReview.js.map +1 -1
  10. package/dist/api/comment.d.ts.map +1 -1
  11. package/dist/api/comment.js +1 -3
  12. package/dist/api/comment.js.map +1 -1
  13. package/dist/api/document.d.ts.map +1 -1
  14. package/dist/api/document.js +8 -4
  15. package/dist/api/document.js.map +1 -1
  16. package/dist/api/feedback.d.ts +1 -1
  17. package/dist/api/feedback.js +1 -1
  18. package/dist/api/linear.d.ts.map +1 -1
  19. package/dist/api/linear.js.map +1 -1
  20. package/dist/api/member.d.ts +1 -1
  21. package/dist/api/plan.d.ts.map +1 -1
  22. package/dist/api/plan.js +1 -3
  23. package/dist/api/plan.js.map +1 -1
  24. package/dist/api/postmortem.d.ts.map +1 -1
  25. package/dist/api/postmortem.js +1 -3
  26. package/dist/api/postmortem.js.map +1 -1
  27. package/dist/api/report.d.ts.map +1 -1
  28. package/dist/api/report.js +1 -3
  29. package/dist/api/report.js.map +1 -1
  30. package/dist/commands/agentConfig.d.ts.map +1 -1
  31. package/dist/commands/agentConfig.js +9 -3
  32. package/dist/commands/agentConfig.js.map +1 -1
  33. package/dist/commands/agentConfigCommand.d.ts.map +1 -1
  34. package/dist/commands/agentConfigCommand.js.map +1 -1
  35. package/dist/commands/attachment.d.ts.map +1 -1
  36. package/dist/commands/attachment.js +31 -31
  37. package/dist/commands/attachment.js.map +1 -1
  38. package/dist/commands/coaction.d.ts.map +1 -1
  39. package/dist/commands/coaction.js +11 -9
  40. package/dist/commands/coaction.js.map +1 -1
  41. package/dist/commands/codeReview.d.ts.map +1 -1
  42. package/dist/commands/codeReview.js +5 -2
  43. package/dist/commands/codeReview.js.map +1 -1
  44. package/dist/commands/convention.d.ts +2 -2
  45. package/dist/commands/convention.d.ts.map +1 -1
  46. package/dist/commands/convention.js +108 -113
  47. package/dist/commands/convention.js.map +1 -1
  48. package/dist/commands/conventionRouter.d.ts.map +1 -1
  49. package/dist/commands/conventionRouter.js.map +1 -1
  50. package/dist/commands/dependency.d.ts.map +1 -1
  51. package/dist/commands/dependency.js +3 -1
  52. package/dist/commands/dependency.js.map +1 -1
  53. package/dist/commands/dependencyCommand.d.ts.map +1 -1
  54. package/dist/commands/dependencyCommand.js.map +1 -1
  55. package/dist/commands/document.d.ts.map +1 -1
  56. package/dist/commands/document.js +79 -83
  57. package/dist/commands/document.js.map +1 -1
  58. package/dist/commands/feedback.js +13 -13
  59. package/dist/commands/index.d.ts.map +1 -1
  60. package/dist/commands/index.js +13 -14
  61. package/dist/commands/index.js.map +1 -1
  62. package/dist/commands/init.d.ts.map +1 -1
  63. package/dist/commands/init.js +7 -8
  64. package/dist/commands/init.js.map +1 -1
  65. package/dist/commands/linear.d.ts.map +1 -1
  66. package/dist/commands/linear.js +1 -1
  67. package/dist/commands/linear.js.map +1 -1
  68. package/dist/commands/plan.d.ts.map +1 -1
  69. package/dist/commands/plan.js +46 -36
  70. package/dist/commands/plan.js.map +1 -1
  71. package/dist/commands/postmortem.d.ts.map +1 -1
  72. package/dist/commands/postmortem.js +6 -4
  73. package/dist/commands/postmortem.js.map +1 -1
  74. package/dist/commands/report.d.ts.map +1 -1
  75. package/dist/commands/report.js +5 -3
  76. package/dist/commands/report.js.map +1 -1
  77. package/dist/commands/search.d.ts.map +1 -1
  78. package/dist/commands/search.js.map +1 -1
  79. package/dist/index.js +5 -4
  80. package/dist/index.js.map +1 -1
  81. package/dist/types/index.d.ts +1 -1
  82. package/dist/utils/atomicWrite.d.ts.map +1 -1
  83. package/dist/utils/atomicWrite.js +2 -2
  84. package/dist/utils/atomicWrite.js.map +1 -1
  85. package/dist/utils/authServer.d.ts.map +1 -1
  86. package/dist/utils/authServer.js +2 -5
  87. package/dist/utils/authServer.js.map +1 -1
  88. package/dist/utils/config.d.ts +1 -1
  89. package/dist/utils/config.d.ts.map +1 -1
  90. package/dist/utils/config.js +9 -15
  91. package/dist/utils/config.js.map +1 -1
  92. package/dist/utils/git.d.ts.map +1 -1
  93. package/dist/utils/git.js +1 -3
  94. package/dist/utils/git.js.map +1 -1
  95. package/dist/utils/httpClient.d.ts +1 -1
  96. package/dist/utils/httpClient.d.ts.map +1 -1
  97. package/dist/utils/httpClient.js +7 -7
  98. package/dist/utils/httpClient.js.map +1 -1
  99. package/dist/utils/initOutput.d.ts.map +1 -1
  100. package/dist/utils/initOutput.js.map +1 -1
  101. package/dist/utils/outputPolicy.d.ts.map +1 -1
  102. package/dist/utils/outputPolicy.js +1 -3
  103. package/dist/utils/outputPolicy.js.map +1 -1
  104. package/dist/utils/parsers.d.ts +7 -1
  105. package/dist/utils/parsers.d.ts.map +1 -1
  106. package/dist/utils/parsers.js +18 -11
  107. package/dist/utils/parsers.js.map +1 -1
  108. package/dist/utils/planFormat.d.ts.map +1 -1
  109. package/dist/utils/planFormat.js +1 -1
  110. package/dist/utils/planFormat.js.map +1 -1
  111. package/dist/utils/planPreviewHtmlSafety.d.ts.map +1 -1
  112. package/dist/utils/planPreviewHtmlSafety.js +15 -15
  113. package/dist/utils/planPreviewHtmlSafety.js.map +1 -1
  114. package/dist/utils/spinner.d.ts.map +1 -1
  115. package/dist/utils/spinner.js.map +1 -1
  116. package/dist/utils/updateCheck.d.ts.map +1 -1
  117. package/dist/utils/updateCheck.js +11 -3
  118. package/dist/utils/updateCheck.js.map +1 -1
  119. package/eslint.config.mjs +75 -79
  120. package/package.json +4 -1
@@ -1,26 +1,26 @@
1
- import { existsSync, mkdirSync, readFileSync, rmSync, unlinkSync } from "node:fs";
2
- import { atomicWriteFileSync } from "../utils/atomicWrite.js";
3
- import { basename, join, relative, resolve, sep } from "node:path";
4
- import httpClient from "../utils/httpClient.js";
5
- import { isAxiosError } from "axios";
6
- import matter from "gray-matter";
7
- import { diffLines, createTwoFilesPatch } from "diff";
8
- import { loadConfig, findProjectConfig } from "../utils/config.js";
9
- import { withSpinner } from "../utils/spinner.js";
10
- import { withoutJsonContentType } from "../utils/httpHeaders.js";
11
- const CONVENTION_DIR = ".agentteams";
12
- const LEGACY_CONVENTION_DOWNLOAD_DIR = "conventions";
13
- const CONVENTION_INDEX_FILE = "convention.md";
14
- const CONVENTION_MANIFEST_FILE = "conventions.manifest.json";
1
+ import { existsSync, mkdirSync, readFileSync, rmSync, unlinkSync } from 'node:fs';
2
+ import { atomicWriteFileSync } from '../utils/atomicWrite.js';
3
+ import { basename, join, relative, resolve, sep } from 'node:path';
4
+ import httpClient from '../utils/httpClient.js';
5
+ import { isAxiosError } from 'axios';
6
+ import matter from 'gray-matter';
7
+ import { diffLines, createTwoFilesPatch } from 'diff';
8
+ import { loadConfig, findProjectConfig } from '../utils/config.js';
9
+ import { withSpinner } from '../utils/spinner.js';
10
+ import { withoutJsonContentType } from '../utils/httpHeaders.js';
11
+ const CONVENTION_DIR = '.agentteams';
12
+ const LEGACY_CONVENTION_DOWNLOAD_DIR = 'conventions';
13
+ const CONVENTION_INDEX_FILE = 'convention.md';
14
+ const CONVENTION_MANIFEST_FILE = 'conventions.manifest.json';
15
15
  function findProjectRoot(cwd) {
16
16
  const configPath = findProjectConfig(cwd ?? process.cwd());
17
17
  if (!configPath)
18
18
  return null;
19
19
  // configPath = /path/.agentteams/config.json → resolve up 2 levels to project root
20
- return resolve(configPath, "..", "..");
20
+ return resolve(configPath, '..', '..');
21
21
  }
22
22
  function getApiBaseUrl(apiUrl) {
23
- return apiUrl.endsWith("/") ? apiUrl.slice(0, -1) : apiUrl;
23
+ return apiUrl.endsWith('/') ? apiUrl.slice(0, -1) : apiUrl;
24
24
  }
25
25
  function getApiConfigOrThrow(options) {
26
26
  const config = options?.config ?? loadConfig();
@@ -31,13 +31,13 @@ function getApiConfigOrThrow(options) {
31
31
  config,
32
32
  apiUrl: getApiBaseUrl(config.apiUrl),
33
33
  headers: {
34
- "X-API-Key": config.apiKey,
35
- "Content-Type": "application/json",
34
+ 'X-API-Key': config.apiKey,
35
+ 'Content-Type': 'application/json',
36
36
  },
37
37
  };
38
38
  }
39
39
  function normalizeRelativePath(input) {
40
- return input.replaceAll("\\", "/");
40
+ return input.replaceAll('\\', '/');
41
41
  }
42
42
  function resolveConventionFileAbsolutePath(projectRoot, cwd, fileInput) {
43
43
  // If absolute path, keep as-is.
@@ -59,7 +59,7 @@ function resolveConventionFileAbsolutePath(projectRoot, cwd, fileInput) {
59
59
  function validatePathBoundary(absolutePath, projectRoot) {
60
60
  const normalized = resolve(absolutePath);
61
61
  if (!normalized.startsWith(resolve(projectRoot) + sep)) {
62
- throw new Error("Path traversal detected: file must be within project root");
62
+ throw new Error('Path traversal detected: file must be within project root');
63
63
  }
64
64
  return normalized;
65
65
  }
@@ -71,7 +71,7 @@ function loadManifestOrThrow(projectRoot) {
71
71
  if (!existsSync(manifestPath)) {
72
72
  throw new Error(`Download manifest not found: ${manifestPath}\nRun 'agentteams convention download' first.`);
73
73
  }
74
- const raw = readFileSync(manifestPath, "utf-8");
74
+ const raw = readFileSync(manifestPath, 'utf-8');
75
75
  const parsed = JSON.parse(raw);
76
76
  if (parsed?.version !== 1 || !Array.isArray(parsed.entries)) {
77
77
  throw new Error(`Invalid manifest format: ${manifestPath}`);
@@ -87,7 +87,7 @@ function loadManifestOrCreate(projectRoot) {
87
87
  entries: [],
88
88
  };
89
89
  }
90
- const raw = readFileSync(manifestPath, "utf-8");
90
+ const raw = readFileSync(manifestPath, 'utf-8');
91
91
  const parsed = JSON.parse(raw);
92
92
  if (parsed?.version !== 1 || !Array.isArray(parsed.entries)) {
93
93
  throw new Error(`Invalid manifest format: ${manifestPath}`);
@@ -96,7 +96,7 @@ function loadManifestOrCreate(projectRoot) {
96
96
  }
97
97
  function writeManifest(projectRoot, manifest) {
98
98
  const manifestPath = buildManifestPath(projectRoot);
99
- atomicWriteFileSync(manifestPath, JSON.stringify(manifest, null, 2) + "\n", "utf-8");
99
+ atomicWriteFileSync(manifestPath, JSON.stringify(manifest, null, 2) + '\n', 'utf-8');
100
100
  }
101
101
  function toFileList(input) {
102
102
  return Array.isArray(input) ? input : [input];
@@ -106,21 +106,19 @@ function hasAnyDiff(a, b) {
106
106
  return parts.some((p) => p.added || p.removed);
107
107
  }
108
108
  function createUnifiedDiff(fileLabel, serverText, localText) {
109
- return createTwoFilesPatch(`${fileLabel} (server)`, `${fileLabel} (local)`, serverText, localText, "", "", { context: 3 });
109
+ return createTwoFilesPatch(`${fileLabel} (server)`, `${fileLabel} (local)`, serverText, localText, '', '', {
110
+ context: 3,
111
+ });
110
112
  }
111
113
  function toOptionalString(value) {
112
- return typeof value === "string" ? value : undefined;
114
+ return typeof value === 'string' ? value : undefined;
113
115
  }
114
116
  function fileNameToTitle(fileName) {
115
- return fileName
116
- .replace(/\.md$/i, "")
117
- .replace(/[-_]+/g, " ")
118
- .replace(/\s+/g, " ")
119
- .trim();
117
+ return fileName.replace(/\.md$/i, '').replace(/[-_]+/g, ' ').replace(/\s+/g, ' ').trim();
120
118
  }
121
119
  function parseCategoryFromAgentteamsPath(fileRelativePath) {
122
120
  const normalized = normalizeRelativePath(fileRelativePath);
123
- const parts = normalized.split("/");
121
+ const parts = normalized.split('/');
124
122
  const agentteamsIndex = parts.indexOf(CONVENTION_DIR);
125
123
  if (agentteamsIndex === -1) {
126
124
  throw new Error(`Convention create requires a file under ${CONVENTION_DIR}/<category>/: ${fileRelativePath}`);
@@ -129,7 +127,7 @@ function parseCategoryFromAgentteamsPath(fileRelativePath) {
129
127
  if (!category || category.length === 0) {
130
128
  throw new Error(`Convention create requires a category directory under ${CONVENTION_DIR}/: ${fileRelativePath}`);
131
129
  }
132
- if (category === "platform" || category === "active-plan") {
130
+ if (category === 'platform' || category === 'active-plan') {
133
131
  throw new Error(`Convention create does not allow reserved directories under ${CONVENTION_DIR}/: ${category}`);
134
132
  }
135
133
  return category;
@@ -140,14 +138,17 @@ async function fetchAllConventions(apiUrl, projectId, headers) {
140
138
  let totalPages;
141
139
  const items = [];
142
140
  while (true) {
143
- const response = await httpClient.get(`${apiUrl}/api/projects/${projectId}/conventions`, { headers, params: { page, pageSize } });
141
+ const response = await httpClient.get(`${apiUrl}/api/projects/${projectId}/conventions`, {
142
+ headers,
143
+ params: { page, pageSize },
144
+ });
144
145
  const data = response.data?.data;
145
146
  if (!Array.isArray(data)) {
146
147
  break;
147
148
  }
148
149
  items.push(...data);
149
150
  const meta = response.data?.meta;
150
- if (typeof meta?.totalPages === "number") {
151
+ if (typeof meta?.totalPages === 'number') {
151
152
  totalPages = meta.totalPages;
152
153
  }
153
154
  if (totalPages !== undefined) {
@@ -167,32 +168,32 @@ async function fetchConventionsWithContent(apiUrl, projectId, headers) {
167
168
  const response = await httpClient.get(`${apiUrl}/api/projects/${projectId}/conventions/download-all`, { headers });
168
169
  const data = response.data?.data;
169
170
  if (!Array.isArray(data)) {
170
- throw new Error("Invalid download-all response format");
171
+ throw new Error('Invalid download-all response format');
171
172
  }
172
173
  return data;
173
174
  }
174
175
  async function fetchPlatformGuidesHash(apiUrl, headers) {
175
176
  const response = await httpClient.get(`${apiUrl}/api/platform/guides/hash`, { headers });
176
177
  const hash = response.data?.data?.hash;
177
- if (typeof hash !== "string" || hash.length === 0) {
178
- throw new Error("Invalid platform guides hash response format");
178
+ if (typeof hash !== 'string' || hash.length === 0) {
179
+ throw new Error('Invalid platform guides hash response format');
179
180
  }
180
181
  return hash;
181
182
  }
182
183
  function toConventionName(convention) {
183
- const title = typeof convention.title === "string" ? convention.title.trim() : "";
184
+ const title = typeof convention.title === 'string' ? convention.title.trim() : '';
184
185
  if (title.length > 0)
185
186
  return title;
186
- const fileName = typeof convention.fileName === "string" ? convention.fileName.trim() : "";
187
+ const fileName = typeof convention.fileName === 'string' ? convention.fileName.trim() : '';
187
188
  if (fileName.length > 0)
188
189
  return fileName;
189
190
  return convention.id;
190
191
  }
191
192
  function toConventionNameFromManifest(entry) {
192
- const title = typeof entry.title === "string" ? entry.title.trim() : "";
193
+ const title = typeof entry.title === 'string' ? entry.title.trim() : '';
193
194
  if (title.length > 0)
194
195
  return title;
195
- const fileName = typeof entry.fileName === "string" ? entry.fileName.trim() : "";
196
+ const fileName = typeof entry.fileName === 'string' ? entry.fileName.trim() : '';
196
197
  if (fileName.length > 0)
197
198
  return fileName;
198
199
  return entry.conventionId;
@@ -205,7 +206,7 @@ function toOptionalStringOrNullIfPresent(data, key) {
205
206
  if (value === null) {
206
207
  return null;
207
208
  }
208
- if (typeof value === "string") {
209
+ if (typeof value === 'string') {
209
210
  const trimmed = value.trim();
210
211
  return trimmed.length > 0 ? trimmed : null;
211
212
  }
@@ -215,15 +216,15 @@ export async function conventionShow() {
215
216
  const { config, apiUrl, headers } = getApiConfigOrThrow();
216
217
  const conventions = await fetchConventionsWithContent(apiUrl, config.projectId, headers);
217
218
  if (!conventions || conventions.length === 0) {
218
- throw new Error("No conventions found for this project. Create one via the web dashboard first.");
219
+ throw new Error('No conventions found for this project. Create one via the web dashboard first.');
219
220
  }
220
221
  const sections = [];
221
222
  for (const convention of conventions) {
222
- const contentMarkdown = typeof convention.contentMarkdown === "string" ? convention.contentMarkdown : "";
223
- const sectionHeader = `# ${convention.title ?? "untitled"}\ncategory: ${convention.category ?? "uncategorized"}\nid: ${convention.id}`;
223
+ const contentMarkdown = typeof convention.contentMarkdown === 'string' ? convention.contentMarkdown : '';
224
+ const sectionHeader = `# ${convention.title ?? 'untitled'}\ncategory: ${convention.category ?? 'uncategorized'}\nid: ${convention.id}`;
224
225
  sections.push(`${sectionHeader}\n\n${contentMarkdown}`);
225
226
  }
226
- return sections.join("\n\n---\n\n");
227
+ return sections.join('\n\n---\n\n');
227
228
  }
228
229
  export async function checkConventionFreshness(apiUrl, projectId, headers, projectRoot) {
229
230
  const manifestPath = buildManifestPath(projectRoot);
@@ -235,9 +236,9 @@ export async function checkConventionFreshness(apiUrl, projectId, headers, proje
235
236
  }
236
237
  const manifest = loadManifestOrThrow(projectRoot);
237
238
  const currentPlatformGuidesHash = await fetchPlatformGuidesHash(apiUrl, headers);
238
- const platformGuidesChanged = typeof manifest.platformGuidesHash === "string"
239
- && manifest.platformGuidesHash.length > 0
240
- && manifest.platformGuidesHash !== currentPlatformGuidesHash;
239
+ const platformGuidesChanged = typeof manifest.platformGuidesHash === 'string' &&
240
+ manifest.platformGuidesHash.length > 0 &&
241
+ manifest.platformGuidesHash !== currentPlatformGuidesHash;
241
242
  const serverConventions = await fetchAllConventions(apiUrl, projectId, headers);
242
243
  const serverById = new Map(serverConventions.map((item) => [item.id, item]));
243
244
  const localById = new Map(manifest.entries.map((entry) => [entry.conventionId, entry]));
@@ -247,18 +248,18 @@ export async function checkConventionFreshness(apiUrl, projectId, headers, proje
247
248
  if (!local) {
248
249
  conventionChanges.push({
249
250
  id: serverConvention.id,
250
- type: "new",
251
+ type: 'new',
251
252
  title: toConventionName(serverConvention),
252
253
  fileName: serverConvention.fileName ?? undefined,
253
254
  });
254
255
  continue;
255
256
  }
256
- if (typeof serverConvention.updatedAt === "string"
257
- && typeof local.updatedAt === "string"
258
- && serverConvention.updatedAt !== local.updatedAt) {
257
+ if (typeof serverConvention.updatedAt === 'string' &&
258
+ typeof local.updatedAt === 'string' &&
259
+ serverConvention.updatedAt !== local.updatedAt) {
259
260
  conventionChanges.push({
260
261
  id: serverConvention.id,
261
- type: "updated",
262
+ type: 'updated',
262
263
  title: toConventionName(serverConvention),
263
264
  fileName: serverConvention.fileName ?? local.fileName,
264
265
  });
@@ -269,7 +270,7 @@ export async function checkConventionFreshness(apiUrl, projectId, headers, proje
269
270
  continue;
270
271
  conventionChanges.push({
271
272
  id: localEntry.conventionId,
272
- type: "deleted",
273
+ type: 'deleted',
273
274
  title: toConventionNameFromManifest(localEntry),
274
275
  fileName: localEntry.fileName,
275
276
  });
@@ -282,7 +283,7 @@ export async function checkConventionFreshness(apiUrl, projectId, headers, proje
282
283
  export function buildStatusSummary(result) {
283
284
  const parts = [];
284
285
  if (result.platformGuidesChanged)
285
- parts.push("platform guides");
286
+ parts.push('platform guides');
286
287
  const counts = { new: 0, updated: 0, deleted: 0 };
287
288
  for (const change of result.conventionChanges)
288
289
  counts[change.type] += 1;
@@ -293,8 +294,8 @@ export function buildStatusSummary(result) {
293
294
  if (counts.deleted > 0)
294
295
  parts.push(`${counts.deleted} deleted`);
295
296
  if (parts.length === 0)
296
- return "✓ Conventions up to date";
297
- return `⚠ Updates available (${parts.join(", ")}). Run 'agentteams convention download' to sync.`;
297
+ return '✓ Conventions up to date';
298
+ return `⚠ Updates available (${parts.join(', ')}). Run 'agentteams convention download' to sync.`;
298
299
  }
299
300
  /**
300
301
  * Read-only freshness check exposed as `agentteams convention status`.
@@ -313,13 +314,13 @@ export async function conventionStatus(options) {
313
314
  updateAvailable: false,
314
315
  platformGuidesChanged: false,
315
316
  conventionChanges: [],
316
- summary: "✓ Conventions up to date",
317
+ summary: '✓ Conventions up to date',
317
318
  };
318
319
  }
319
320
  const apiUrl = getApiBaseUrl(config.apiUrl);
320
321
  const headers = {
321
- "X-API-Key": config.apiKey,
322
- "Content-Type": "application/json",
322
+ 'X-API-Key': config.apiKey,
323
+ 'Content-Type': 'application/json',
323
324
  };
324
325
  const freshness = await checkConventionFreshness(apiUrl, config.projectId, headers, projectRoot);
325
326
  return {
@@ -349,33 +350,31 @@ export async function conventionList() {
349
350
  page: 1,
350
351
  pageSize: conventions.length,
351
352
  totalPages: 1,
352
- }
353
+ },
353
354
  };
354
355
  }
355
356
  function toSafeFileName(input) {
356
357
  return input
357
358
  .toLowerCase()
358
- .replace(/[^a-z0-9]+/g, "-")
359
- .replace(/^-+|-+$/g, "")
359
+ .replace(/[^a-z0-9]+/g, '-')
360
+ .replace(/^-+|-+$/g, '')
360
361
  .slice(0, 60);
361
362
  }
362
363
  function toSafeDirectoryName(input) {
363
364
  const normalized = toSafeFileName(input);
364
- return normalized.length > 0 ? normalized : "uncategorized";
365
+ return normalized.length > 0 ? normalized : 'uncategorized';
365
366
  }
366
367
  function buildConventionFileName(convention) {
367
368
  if (convention.fileName && convention.fileName.trim().length > 0) {
368
369
  return convention.fileName.trim();
369
370
  }
370
- const titleSegment = convention.title ? toSafeFileName(convention.title) : "";
371
- const prefix = titleSegment.length > 0 ? titleSegment : "convention";
371
+ const titleSegment = convention.title ? toSafeFileName(convention.title) : '';
372
+ const prefix = titleSegment.length > 0 ? titleSegment : 'convention';
372
373
  return `${prefix}.md`;
373
374
  }
374
375
  function normalizeMarkdownFileName(input) {
375
376
  const trimmed = input.trim();
376
- const base = trimmed.toLowerCase().endsWith('.md')
377
- ? trimmed.slice(0, -3)
378
- : trimmed;
377
+ const base = trimmed.toLowerCase().endsWith('.md') ? trimmed.slice(0, -3) : trimmed;
379
378
  const safeBase = toSafeFileName(base);
380
379
  const resolvedBase = safeBase.length > 0 ? safeBase : 'guide';
381
380
  return `${resolvedBase}.md`;
@@ -408,9 +407,7 @@ async function downloadPlatformGuides(projectRoot, apiUrl, headers) {
408
407
  const baseFileName = buildPlatformGuideFileName(guide);
409
408
  const seenCount = fileNameCount.get(baseFileName) ?? 0;
410
409
  fileNameCount.set(baseFileName, seenCount + 1);
411
- const fileName = seenCount === 0
412
- ? baseFileName
413
- : baseFileName.replace(/\.md$/, `-${seenCount + 1}.md`);
410
+ const fileName = seenCount === 0 ? baseFileName : baseFileName.replace(/\.md$/, `-${seenCount + 1}.md`);
414
411
  const filePath = join(baseDir, fileName);
415
412
  atomicWriteFileSync(filePath, guide.content, 'utf-8');
416
413
  written += 1;
@@ -425,22 +422,24 @@ async function downloadPlatformGuides(projectRoot, apiUrl, headers) {
425
422
  }
426
423
  }
427
424
  async function downloadReportingTemplate(projectRoot, config, apiUrl, headers) {
428
- const agentConfigResponse = await httpClient.get(`${apiUrl}/api/projects/${config.projectId}/agent-configs`, { headers });
425
+ const agentConfigResponse = await httpClient.get(`${apiUrl}/api/projects/${config.projectId}/agent-configs`, {
426
+ headers,
427
+ });
429
428
  const agentConfigs = agentConfigResponse.data?.data;
430
429
  if (!Array.isArray(agentConfigs) || agentConfigs.length === 0) {
431
430
  return false;
432
431
  }
433
432
  const firstAgentConfig = agentConfigs[0];
434
- if (!firstAgentConfig?.id || typeof firstAgentConfig.id !== "string") {
433
+ if (!firstAgentConfig?.id || typeof firstAgentConfig.id !== 'string') {
435
434
  return false;
436
435
  }
437
436
  const templateResponse = await httpClient.get(`${apiUrl}/api/projects/${config.projectId}/agent-configs/${firstAgentConfig.id}/convention`, { headers });
438
437
  const content = templateResponse.data?.data?.content;
439
- if (typeof content !== "string") {
438
+ if (typeof content !== 'string') {
440
439
  return false;
441
440
  }
442
441
  const conventionPath = join(projectRoot, CONVENTION_DIR, CONVENTION_INDEX_FILE);
443
- atomicWriteFileSync(conventionPath, content, "utf-8");
442
+ atomicWriteFileSync(conventionPath, content, 'utf-8');
444
443
  return true;
445
444
  }
446
445
  export async function conventionDownload(options) {
@@ -464,7 +463,7 @@ export async function conventionDownload(options) {
464
463
  rmSync(legacyDir, { recursive: true, force: true });
465
464
  const categoryDirs = new Set();
466
465
  for (const convention of conventionList) {
467
- const categoryName = typeof convention.category === "string" ? convention.category : "";
466
+ const categoryName = typeof convention.category === 'string' ? convention.category : '';
468
467
  categoryDirs.add(toSafeDirectoryName(categoryName));
469
468
  }
470
469
  for (const categoryDir of categoryDirs) {
@@ -480,18 +479,16 @@ export async function conventionDownload(options) {
480
479
  entries: [],
481
480
  };
482
481
  for (const convention of conventionList) {
483
- const contentMarkdown = typeof convention.contentMarkdown === "string" ? convention.contentMarkdown : "";
482
+ const contentMarkdown = typeof convention.contentMarkdown === 'string' ? convention.contentMarkdown : '';
484
483
  const baseFileName = buildConventionFileName(convention);
485
- const categoryName = typeof convention.category === "string" ? convention.category : "";
484
+ const categoryName = typeof convention.category === 'string' ? convention.category : '';
486
485
  const categoryDir = toSafeDirectoryName(categoryName);
487
486
  const duplicateKey = `${categoryDir}/${baseFileName}`;
488
487
  const seenCount = fileNameCount.get(duplicateKey) ?? 0;
489
488
  fileNameCount.set(duplicateKey, seenCount + 1);
490
- const fileName = seenCount === 0
491
- ? baseFileName
492
- : baseFileName.replace(/\.md$/, `-${seenCount + 1}.md`);
489
+ const fileName = seenCount === 0 ? baseFileName : baseFileName.replace(/\.md$/, `-${seenCount + 1}.md`);
493
490
  const filePath = join(projectRoot, CONVENTION_DIR, categoryDir, fileName);
494
- atomicWriteFileSync(filePath, contentMarkdown, "utf-8");
491
+ atomicWriteFileSync(filePath, contentMarkdown, 'utf-8');
495
492
  manifest.entries.push({
496
493
  conventionId: String(convention.id),
497
494
  fileRelativePath: normalizeRelativePath(relative(projectRoot, filePath)),
@@ -514,14 +511,12 @@ export async function conventionDownload(options) {
514
511
  : '';
515
512
  return `Convention sync completed.\nUpdated ${CONVENTION_DIR}/${CONVENTION_INDEX_FILE}\nNo project conventions found.${platformLine}`;
516
513
  }
517
- throw new Error("No conventions found for this project. Create one via the web dashboard first.");
514
+ throw new Error('No conventions found for this project. Create one via the web dashboard first.');
518
515
  }
519
- const reportingLine = hasReportingTemplate
520
- ? `Updated ${CONVENTION_DIR}/${CONVENTION_INDEX_FILE}\n`
521
- : "";
516
+ const reportingLine = hasReportingTemplate ? `Updated ${CONVENTION_DIR}/${CONVENTION_INDEX_FILE}\n` : '';
522
517
  const platformLine = platformGuideCount > 0
523
518
  ? `Downloaded ${platformGuideCount} platform guide file(s) into ${CONVENTION_DIR}/platform\n`
524
- : "";
519
+ : '';
525
520
  return `Convention sync completed.\n${reportingLine}${platformLine}Downloaded ${conventions.length} file(s) into category directories under ${CONVENTION_DIR}`;
526
521
  }
527
522
  function parseConventionMarkdown(fileRelativePath, markdown) {
@@ -551,7 +546,7 @@ export async function conventionCreate(options) {
551
546
  const fileRelativePath = normalizeRelativePath(relative(projectRoot, absolutePath));
552
547
  const category = parseCategoryFromAgentteamsPath(fileRelativePath);
553
548
  const fileName = basename(absolutePath);
554
- if (!fileName.toLowerCase().endsWith(".md")) {
549
+ if (!fileName.toLowerCase().endsWith('.md')) {
555
550
  throw new Error(`Convention create requires a .md file: ${fileRelativePath}`);
556
551
  }
557
552
  const existingEntry = manifest.entries.find((e) => e.fileRelativePath === fileRelativePath);
@@ -559,10 +554,10 @@ export async function conventionCreate(options) {
559
554
  throw new Error(`File is already tracked in the manifest (use update instead): ${fileRelativePath}\n` +
560
555
  `- conventionId: ${existingEntry.conventionId}`);
561
556
  }
562
- const localMarkdown = readFileSync(absolutePath, "utf-8");
557
+ const localMarkdown = readFileSync(absolutePath, 'utf-8');
563
558
  const parsed = parseConventionMarkdown(fileRelativePath, localMarkdown);
564
559
  const frontmatter = (parsed.data ?? {});
565
- const bodyMarkdown = String(parsed.content ?? "");
560
+ const bodyMarkdown = String(parsed.content ?? '');
566
561
  const content = bodyMarkdown;
567
562
  const title = toOptionalString(frontmatter.title)?.trim() || fileNameToTitle(fileName);
568
563
  const payload = {
@@ -578,14 +573,14 @@ export async function conventionCreate(options) {
578
573
  payload.trigger = trigger;
579
574
  if (description)
580
575
  payload.description = description;
581
- if (typeof agentInstruction === "string" && agentInstruction.trim().length > 0) {
576
+ if (typeof agentInstruction === 'string' && agentInstruction.trim().length > 0) {
582
577
  payload.agentInstruction = agentInstruction.trimEnd();
583
578
  }
584
579
  const response = await withSpinner(`Creating convention for ${fileRelativePath}...`, () => httpClient.post(`${apiUrl}/api/projects/${config.projectId}/conventions`, payload, { headers }));
585
580
  const created = response.data?.data;
586
- const createdId = typeof created?.id === "string" ? created.id : "unknown";
587
- const createdUpdatedAt = typeof created?.updatedAt === "string" ? created.updatedAt : undefined;
588
- const createdWebUrl = typeof created?.webUrl === "string" ? created.webUrl : undefined;
581
+ const createdId = typeof created?.id === 'string' ? created.id : 'unknown';
582
+ const createdUpdatedAt = typeof created?.updatedAt === 'string' ? created.updatedAt : undefined;
583
+ const createdWebUrl = typeof created?.webUrl === 'string' ? created.webUrl : undefined;
589
584
  const now = new Date().toISOString();
590
585
  manifest.generatedAt = now;
591
586
  manifest.entries.push({
@@ -608,7 +603,7 @@ export async function conventionCreate(options) {
608
603
  results.push(`[OK] ${CONVENTION_DIR}/${CONVENTION_MANIFEST_FILE}: Updated.`);
609
604
  results.push(`[NEXT] Run 'agentteams convention download' to refresh convention.md and canonical markdown.`);
610
605
  }
611
- return results.join("\n");
606
+ return results.join('\n');
612
607
  }
613
608
  export async function conventionUpdate(options) {
614
609
  const { config, apiUrl, headers } = getApiConfigOrThrow(options);
@@ -633,13 +628,13 @@ export async function conventionUpdate(options) {
633
628
  throw new Error(`Only downloaded convention files can be updated: ${fileInput}\n` +
634
629
  `- resolved: ${fileRelativePath}\n` +
635
630
  `Run 'agentteams convention download' first, or pass a file path listed in the manifest.\n` +
636
- (available.length > 0 ? `Examples (partial):\n- ${available.join("\n- ")}` : ""));
631
+ (available.length > 0 ? `Examples (partial):\n- ${available.join('\n- ')}` : ''));
637
632
  }
638
633
  const conventionId = manifestEntry.conventionId;
639
634
  const [serverDetail, serverMarkdown, localMarkdown] = await withSpinner(`Preparing update for ${fileRelativePath}...`, async () => {
640
635
  const detailResponse = await httpClient.get(`${apiUrl}/api/projects/${config.projectId}/conventions/${conventionId}`, { headers });
641
- const downloadResponse = await httpClient.get(`${apiUrl}/api/projects/${config.projectId}/conventions/${conventionId}/download`, { headers, responseType: "text" });
642
- const local = readFileSync(absolutePath, "utf-8");
636
+ const downloadResponse = await httpClient.get(`${apiUrl}/api/projects/${config.projectId}/conventions/${conventionId}/download`, { headers, responseType: 'text' });
637
+ const local = readFileSync(absolutePath, 'utf-8');
643
638
  return [detailResponse.data?.data, String(downloadResponse.data), local];
644
639
  });
645
640
  if (!hasAnyDiff(serverMarkdown, localMarkdown)) {
@@ -654,18 +649,18 @@ export async function conventionUpdate(options) {
654
649
  }
655
650
  const parsed = parseConventionMarkdown(fileRelativePath, localMarkdown);
656
651
  const frontmatter = (parsed.data ?? {});
657
- const bodyMarkdown = String(parsed.content ?? "");
652
+ const bodyMarkdown = String(parsed.content ?? '');
658
653
  const content = bodyMarkdown;
659
- if (typeof serverDetail?.updatedAt !== "string" || serverDetail.updatedAt.length === 0) {
654
+ if (typeof serverDetail?.updatedAt !== 'string' || serverDetail.updatedAt.length === 0) {
660
655
  throw new Error(`[ERROR] ${fileRelativePath}: Server response is missing updatedAt.`);
661
656
  }
662
657
  const payload = {
663
658
  updatedAt: serverDetail.updatedAt,
664
659
  content,
665
660
  };
666
- const trigger = toOptionalStringOrNullIfPresent(frontmatter, "trigger");
667
- const description = toOptionalStringOrNullIfPresent(frontmatter, "description");
668
- const agentInstruction = toOptionalStringOrNullIfPresent(frontmatter, "agentInstruction");
661
+ const trigger = toOptionalStringOrNullIfPresent(frontmatter, 'trigger');
662
+ const description = toOptionalStringOrNullIfPresent(frontmatter, 'description');
663
+ const agentInstruction = toOptionalStringOrNullIfPresent(frontmatter, 'agentInstruction');
669
664
  if (trigger !== undefined)
670
665
  payload.trigger = trigger;
671
666
  if (description !== undefined)
@@ -674,12 +669,10 @@ export async function conventionUpdate(options) {
674
669
  payload.agentInstruction = agentInstruction;
675
670
  const updatedResponse = await withSpinner(`Uploading ${fileRelativePath}...`, () => httpClient.put(`${apiUrl}/api/projects/${config.projectId}/conventions/${conventionId}`, payload, { headers }));
676
671
  const newUpdatedAt = updatedResponse.data?.data?.updatedAt;
677
- const newWebUrl = typeof updatedResponse.data?.data?.webUrl === "string"
678
- ? updatedResponse.data.data.webUrl
679
- : undefined;
672
+ const newWebUrl = typeof updatedResponse.data?.data?.webUrl === 'string' ? updatedResponse.data.data.webUrl : undefined;
680
673
  const now = new Date().toISOString();
681
674
  manifestEntry.lastUploadedAt = now;
682
- if (typeof newUpdatedAt === "string") {
675
+ if (typeof newUpdatedAt === 'string') {
683
676
  manifestEntry.lastKnownUpdatedAt = newUpdatedAt;
684
677
  }
685
678
  writeManifest(projectRoot, manifest);
@@ -688,7 +681,7 @@ export async function conventionUpdate(options) {
688
681
  results.push(`webUrl: ${newWebUrl}`);
689
682
  }
690
683
  }
691
- return results.join("\n\n");
684
+ return results.join('\n\n');
692
685
  }
693
686
  export async function conventionDelete(options) {
694
687
  const { config, apiUrl, headers } = getApiConfigOrThrow(options);
@@ -713,7 +706,7 @@ export async function conventionDelete(options) {
713
706
  throw new Error(`Only downloaded convention files can be deleted: ${fileInput}\n` +
714
707
  `- resolved: ${fileRelativePath}\n` +
715
708
  `Run 'agentteams convention download' first, or pass a file path listed in the manifest.\n` +
716
- (available.length > 0 ? `Examples (partial):\n- ${available.join("\n- ")}` : ""));
709
+ (available.length > 0 ? `Examples (partial):\n- ${available.join('\n- ')}` : ''));
717
710
  }
718
711
  const entry = manifest.entries[entryIndex];
719
712
  const conventionId = entry.conventionId;
@@ -722,7 +715,9 @@ export async function conventionDelete(options) {
722
715
  results.push(`[DRY-RUN] ${fileRelativePath}: Planned only (no server delete).`);
723
716
  continue;
724
717
  }
725
- await withSpinner(`Deleting convention for ${fileRelativePath}...`, () => httpClient.delete(`${apiUrl}/api/projects/${config.projectId}/conventions/${conventionId}`, { headers: withoutJsonContentType(headers) }));
718
+ await withSpinner(`Deleting convention for ${fileRelativePath}...`, () => httpClient.delete(`${apiUrl}/api/projects/${config.projectId}/conventions/${conventionId}`, {
719
+ headers: withoutJsonContentType(headers),
720
+ }));
726
721
  // After a successful server delete, also clean up local files/manifest.
727
722
  try {
728
723
  unlinkSync(absolutePath);
@@ -734,6 +729,6 @@ export async function conventionDelete(options) {
734
729
  writeManifest(projectRoot, manifest);
735
730
  results.push(`[OK] ${fileRelativePath}: Deleted. (conventionId=${conventionId})`);
736
731
  }
737
- return results.join("\n");
732
+ return results.join('\n');
738
733
  }
739
734
  //# sourceMappingURL=convention.js.map