@deployxai/dxc 0.2.0 → 0.2.2

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 (35) hide show
  1. package/README.md +4 -2
  2. package/dist/index.js +244 -45
  3. package/package.json +1 -1
  4. package/skills/dxc-article-outline/SKILL.md +28 -0
  5. package/skills/dxc-article-outline/agents/openai.yaml +6 -0
  6. package/skills/dxc-article-outline/references/outline-methods.md +57 -0
  7. package/skills/dxc-article-write/SKILL.md +39 -0
  8. package/skills/dxc-article-write/agents/openai.yaml +6 -0
  9. package/skills/dxc-article-write/references/writing-methods.md +62 -0
  10. package/skills/dxc-content-brief/SKILL.md +33 -0
  11. package/skills/dxc-content-brief/agents/openai.yaml +6 -0
  12. package/skills/dxc-content-brief/references/brief-method.md +52 -0
  13. package/skills/dxc-content-review/SKILL.md +35 -0
  14. package/skills/dxc-content-review/agents/openai.yaml +6 -0
  15. package/skills/dxc-content-review/references/review-checklist.md +51 -0
  16. package/skills/dxc-content-workflow/SKILL.md +40 -11
  17. package/skills/dxc-content-workflow/agents/openai.yaml +2 -2
  18. package/skills/dxc-content-workflow/references/stages.md +6 -4
  19. package/skills/dxc-knowledge/SKILL.md +1 -1
  20. package/skills/dxc-memory/SKILL.md +1 -1
  21. package/skills/dxc-profile/SKILL.md +1 -1
  22. package/skills/dxc-project-overview/SKILL.md +26 -0
  23. package/skills/dxc-project-overview/agents/openai.yaml +6 -0
  24. package/skills/dxc-quote-curator/SKILL.md +1 -1
  25. package/skills/dxc-research/SKILL.md +39 -0
  26. package/skills/dxc-research/agents/openai.yaml +6 -0
  27. package/skills/dxc-research/references/research-method.md +77 -0
  28. package/skills/dxc-title-write/SKILL.md +34 -0
  29. package/skills/dxc-title-write/agents/openai.yaml +6 -0
  30. package/skills/dxc-title-write/references/title-methods.md +41 -0
  31. package/skills/dxc-visual-plan/SKILL.md +43 -0
  32. package/skills/dxc-visual-plan/agents/openai.yaml +6 -0
  33. package/skills/dxc-visual-plan/references/visual-methods.md +53 -0
  34. package/skills/dxc-wechat-publisher/SKILL.md +39 -0
  35. package/skills/dxc-wechat-publisher/agents/openai.yaml +6 -0
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # DeployX 内容工作台(DxC)
2
2
 
3
- 这是 DxC 的官方 CLI 与五个领域 Skill。
3
+ 这是 DxC 的官方 CLI 14 个领域 Skill。
4
4
 
5
5
  安装、权限范围、Windows/WorkBuddy 使用方式与源码核验,请阅读:
6
6
  https://deployxai.com/dxc/
@@ -8,7 +8,9 @@ https://deployxai.com/dxc/
8
8
  ## 包含内容
9
9
 
10
10
  - `dxc` CLI:本地文章工作流、画像、内容记忆、历史知识库、金句库和微信草稿交付;
11
- - `dxc-content-workflow`:文章生产的唯一入口;
11
+ - `dxc-content-workflow`:八步文章生产总控;
12
+ - 八个阶段 Skill:可独立从研究、Brief、大纲、正文、标题、视觉、审校或微信交付进入;
13
+ - `dxc-project-overview`:本机已登记项目总览;
12
14
  - `dxc-profile`、`dxc-memory`、`dxc-knowledge`、`dxc-quote-curator`:四个长期资产入口。
13
15
 
14
16
  安装本包不会自动登录微信、绑定公众号、读取历史文章、上传内容或创建草稿。每项敏感操作
package/dist/index.js CHANGED
@@ -32,6 +32,7 @@ import {
32
32
  createWechatLoginSessionResponseSchema,
33
33
  deviceSessionStatusResponseSchema,
34
34
  doctorResultSchema,
35
+ ensurePrivateDirectory,
35
36
  portableRelativePathSchema,
36
37
  preflightResultSchema,
37
38
  proveDeviceBindingSessionResponseSchema,
@@ -23265,7 +23266,7 @@ import os3 from "node:os";
23265
23266
  // apps/cli/package.json
23266
23267
  var package_default = {
23267
23268
  name: "@dxc/cli",
23268
- version: "0.2.0",
23269
+ version: "0.2.2",
23269
23270
  private: true,
23270
23271
  type: "module",
23271
23272
  bin: {
@@ -27177,6 +27178,7 @@ var MANIFEST_NAME = "dxc.project.json";
27177
27178
  var MANIFEST_MAXIMUM_BYTES = 32 * 1024;
27178
27179
  var PROJECT_INDEX_MAXIMUM_BYTES = 512 * 1024;
27179
27180
  var PROJECT_INDEX_RELATIVE_PATH = ".dxc/content-projects.json";
27181
+ var DEFAULT_PROJECTS_RELATIVE_PATH = ".dxc/projects";
27180
27182
  var DEFAULT_ARTIFACT_PATHS = {
27181
27183
  article: "artifacts/04-article.md",
27182
27184
  brief: "artifacts/02-brief.md",
@@ -27268,7 +27270,8 @@ var ProjectCli = class {
27268
27270
  await writePrivateJson(indexPath(this.#homeDirectory), { projects, schemaVersion: "1" });
27269
27271
  }
27270
27272
  async initialize(directoryInput, title) {
27271
- const directory = resolveUserPath({
27273
+ const projectId = this.#randomUUID();
27274
+ const directory = directoryInput === void 0 ? path3.join(this.#homeDirectory, DEFAULT_PROJECTS_RELATIVE_PATH, projectId) : resolveUserPath({
27272
27275
  currentDirectory: this.#currentDirectory,
27273
27276
  homeDirectory: this.#homeDirectory,
27274
27277
  input: directoryInput
@@ -27289,7 +27292,7 @@ var ProjectCli = class {
27289
27292
  artifacts: DEFAULT_ARTIFACT_PATHS,
27290
27293
  assetsDirectory: "assets",
27291
27294
  createdAt: this.#now().toISOString(),
27292
- projectId: this.#randomUUID(),
27295
+ projectId,
27293
27296
  schemaVersion: "1",
27294
27297
  title,
27295
27298
  workflowId: CONTENT_WORKFLOW_ID
@@ -27298,8 +27301,14 @@ var ProjectCli = class {
27298
27301
  throw new ProjectCliError("DXC_PROJECT_INVALID", "\u9879\u76EE\u6807\u9898\u6216\u76EE\u5F55\u4E0D\u5408\u6CD5\u3002");
27299
27302
  }
27300
27303
  try {
27301
- await mkdir(path3.join(directory, "artifacts"), { recursive: true });
27302
- await mkdir(path3.join(directory, parsed.data.assetsDirectory), { recursive: true });
27304
+ if (directoryInput === void 0) {
27305
+ await ensurePrivateDirectory(directory);
27306
+ await ensurePrivateDirectory(path3.join(directory, "artifacts"));
27307
+ await ensurePrivateDirectory(path3.join(directory, parsed.data.assetsDirectory));
27308
+ } else {
27309
+ await mkdir(path3.join(directory, "artifacts"), { recursive: true });
27310
+ await mkdir(path3.join(directory, parsed.data.assetsDirectory), { recursive: true });
27311
+ }
27303
27312
  await writePrivateJson(manifestTarget, parsed.data);
27304
27313
  await this.#remember(directory, parsed.data);
27305
27314
  } catch (error) {
@@ -27308,7 +27317,22 @@ var ProjectCli = class {
27308
27317
  }
27309
27318
  throw new ProjectCliError("DXC_PROJECT_IO_FAILED", "\u65E0\u6CD5\u521B\u5EFA\u5185\u5BB9\u9879\u76EE\u3002");
27310
27319
  }
27311
- return { data: { manifest: parsed.data } };
27320
+ return { data: { directory, manifest: parsed.data } };
27321
+ }
27322
+ async locateByProjectId(projectIdInput) {
27323
+ const projectId = projectIdInput.trim().toLocaleLowerCase("en-US");
27324
+ const index = await readIndex(this.#homeDirectory);
27325
+ const entry = index.projects.find(
27326
+ (candidate) => candidate.projectId.toLocaleLowerCase("en-US") === projectId
27327
+ );
27328
+ if (entry === void 0) {
27329
+ throw new ProjectCliError("DXC_PROJECT_NOT_FOUND", "\u6CA1\u6709\u627E\u5230\u6307\u5B9A\u7684\u5185\u5BB9\u9879\u76EE\u3002");
27330
+ }
27331
+ const manifest = await readManifest(entry.directory);
27332
+ if (manifest.projectId !== entry.projectId) {
27333
+ throw new ProjectCliError("DXC_PROJECT_INVALID", "\u672C\u5730\u9879\u76EE\u767B\u8BB0\u4E0E\u9879\u76EE\u6587\u4EF6\u4E0D\u4E00\u81F4\u3002");
27334
+ }
27335
+ return { directory: entry.directory, manifest };
27312
27336
  }
27313
27337
  async locate(directoryInput) {
27314
27338
  if (directoryInput !== void 0) {
@@ -27368,6 +27392,25 @@ var ProjectCli = class {
27368
27392
  }
27369
27393
  return locations;
27370
27394
  }
27395
+ async list() {
27396
+ const index = await readIndex(this.#homeDirectory);
27397
+ const projects = [];
27398
+ for (const entry of index.projects) {
27399
+ try {
27400
+ const manifest = await readManifest(entry.directory);
27401
+ projects.push({
27402
+ entry,
27403
+ ...manifest.projectId === entry.projectId ? { location: { directory: entry.directory, manifest } } : {}
27404
+ });
27405
+ } catch (error) {
27406
+ if (!(error instanceof ProjectCliError)) {
27407
+ throw error;
27408
+ }
27409
+ projects.push({ entry });
27410
+ }
27411
+ }
27412
+ return projects;
27413
+ }
27371
27414
  };
27372
27415
 
27373
27416
  // apps/cli/src/skills.ts
@@ -27377,6 +27420,15 @@ import path4 from "node:path";
27377
27420
  import { fileURLToPath } from "node:url";
27378
27421
  var OFFICIAL_SKILLS = [
27379
27422
  "dxc-content-workflow",
27423
+ "dxc-research",
27424
+ "dxc-content-brief",
27425
+ "dxc-article-outline",
27426
+ "dxc-article-write",
27427
+ "dxc-title-write",
27428
+ "dxc-visual-plan",
27429
+ "dxc-content-review",
27430
+ "dxc-wechat-publisher",
27431
+ "dxc-project-overview",
27380
27432
  "dxc-profile",
27381
27433
  "dxc-memory",
27382
27434
  "dxc-knowledge",
@@ -30123,13 +30175,22 @@ async function plannedVisualAssets(visualPlanFile, assetsDirectoryInput) {
30123
30175
  [assetsDirectory],
30124
30176
  "inline-image"
30125
30177
  );
30178
+ const sourceLogicalPath = asset["sourcePath"];
30179
+ if (sourceLogicalPath !== void 0 && (typeof sourceLogicalPath !== "string" || !isSafeLogicalAssetPath(sourceLogicalPath))) {
30180
+ throw new WechatCliError(
30181
+ "DXC_PUBLISHING_VISUAL_PLAN_INVALID",
30182
+ "\u6B63\u6587\u914D\u56FE\u7684\u539F\u59CB\u8DEF\u5F84\u5FC5\u987B\u662F\u9879\u76EE\u5185\u5B89\u5168\u76F8\u5BF9\u8DEF\u5F84\u3002"
30183
+ );
30184
+ }
30126
30185
  const digest = createHash4("sha256").update(absolutePath).digest("hex").slice(0, 12);
30127
30186
  const fileName = safeFileName(path5.basename(absolutePath)) || `visual-${digest}.png`;
30128
30187
  return {
30129
30188
  absolutePath,
30130
30189
  alt: typeof asset["purpose"] === "string" ? asset["purpose"] : path5.basename(absolutePath, path5.extname(absolutePath)),
30131
30190
  logicalPath: `assets/dxc-visuals/${digest}-${fileName}`,
30132
- placementAnchor: asset["placementAnchor"]
30191
+ placementAnchor: asset["placementAnchor"],
30192
+ plannedPath: asset["path"],
30193
+ ...sourceLogicalPath === void 0 ? {} : { sourceLogicalPath }
30133
30194
  };
30134
30195
  })
30135
30196
  )
@@ -30175,30 +30236,45 @@ async function preparePublishingDraft(input) {
30175
30236
  const availableByFileName = new Map(
30176
30237
  availableImages.map((candidate) => [path5.basename(candidate), candidate])
30177
30238
  );
30178
- const resolveDeclaredAsset = (logicalPath) => availableByFileName.get(path5.posix.basename(logicalPath)) ?? path5.resolve(path5.dirname(articleInput.path), logicalPath);
30239
+ const projectDirectory = input.assetsDirectory === void 0 ? articleDirectory : commonDirectory(articleDirectory, path5.resolve(input.assetsDirectory));
30240
+ const resolveDeclaredAsset = (logicalPath) => availableByFileName.get(path5.posix.basename(logicalPath)) ?? path5.resolve(projectDirectory, logicalPath);
30179
30241
  const referenced = new Set(logicalInlinePaths(originalBundle));
30180
30242
  const automaticAssetDirectories = [
30181
30243
  articleDirectory,
30182
30244
  ...input.assetsDirectory === void 0 ? [] : [path5.resolve(input.assetsDirectory)]
30183
30245
  ];
30184
- const referencedFilesByLogicalPath = new Map(
30185
- [...referenced].map((logicalPath) => [logicalPath, resolveDeclaredAsset(logicalPath)])
30186
- );
30187
- const referencedFiles = new Set(referencedFilesByLogicalPath.values());
30188
30246
  const articleCoverCandidate = originalBundle.coverImage !== void 0 && isSafeLogicalAssetPath(originalBundle.coverImage) ? resolveDeclaredAsset(originalBundle.coverImage) : void 0;
30189
30247
  const namedCoverCandidate = availableImages.find(
30190
30248
  (candidate) => /^cover(?:[._-]|$)/iu.test(path5.basename(candidate))
30191
30249
  );
30192
30250
  const explicitCover = input.coverFile !== void 0 && input.coverFile !== "auto" ? input.coverFile : void 0;
30193
30251
  const planned = input.visualPlanFile === void 0 ? void 0 : await plannedVisualAssets(input.visualPlanFile, input.assetsDirectory);
30252
+ const plannedInline = planned?.inline ?? [];
30253
+ const plannedReferenceFiles = /* @__PURE__ */ new Map();
30254
+ const plannedInlineForInjection = plannedInline.filter((asset) => {
30255
+ const referencedLogicalPath = [asset.sourceLogicalPath, asset.plannedPath].find(
30256
+ (candidate) => candidate !== void 0 && referenced.has(candidate)
30257
+ );
30258
+ if (referencedLogicalPath === void 0) {
30259
+ return true;
30260
+ }
30261
+ plannedReferenceFiles.set(referencedLogicalPath, asset.absolutePath);
30262
+ return false;
30263
+ });
30264
+ const referencedFilesByLogicalPath = new Map(
30265
+ [...referenced].map((logicalPath) => [
30266
+ logicalPath,
30267
+ plannedReferenceFiles.get(logicalPath) ?? resolveDeclaredAsset(logicalPath)
30268
+ ])
30269
+ );
30270
+ const referencedFiles = new Set(referencedFilesByLogicalPath.values());
30194
30271
  const coverCandidate = explicitCover ?? planned?.coverAbsolutePath ?? articleCoverCandidate ?? namedCoverCandidate;
30195
30272
  const coverMode = explicitCover !== void 0 ? "explicit-file" : planned?.coverAbsolutePath !== void 0 ? "visual-plan" : articleCoverCandidate !== void 0 ? "article-frontmatter" : namedCoverCandidate !== void 0 ? "assets-directory" : void 0;
30196
- const plannedInline = planned?.inline ?? [];
30197
30273
  const pickupCandidates = availableImages.filter(
30198
30274
  (candidate) => candidate !== namedCoverCandidate && candidate !== articleCoverCandidate && !referencedFiles.has(path5.resolve(candidate)) && input.visualPlanFile === void 0
30199
30275
  );
30200
30276
  const injected = [
30201
- ...plannedInline,
30277
+ ...plannedInlineForInjection,
30202
30278
  ...pickupCandidates.slice(0, Math.max(0, MAXIMUM_INLINE_IMAGES - referenced.size)).map((candidate) => {
30203
30279
  const digest = createHash4("sha256").update(candidate).digest("hex").slice(0, 12);
30204
30280
  const fileName = safeFileName(path5.basename(candidate)) || `visual-${digest}.png`;
@@ -30607,6 +30683,9 @@ async function optimizePublishingImages(input) {
30607
30683
  frontmatter2["coverAsset"] = finalPath;
30608
30684
  frontmatter2["coverSha256"] = sha256(finalBytes);
30609
30685
  } else if (entry.record !== void 0) {
30686
+ if (finalPath !== assetPath && entry.record["sourcePath"] === void 0) {
30687
+ entry.record["sourcePath"] = assetPath;
30688
+ }
30610
30689
  entry.record["path"] = finalPath;
30611
30690
  entry.record["sha256"] = sha256(finalBytes);
30612
30691
  }
@@ -31374,7 +31453,7 @@ var WechatCli = class {
31374
31453
  `);
31375
31454
  output.writeStderr(
31376
31455
  result.data.mutations.length === 0 ? `\u56FE\u7247\u5747\u5DF2\u7B26\u5408\u5FAE\u4FE1\u4EA4\u4ED8\u4F53\u79EF\u8981\u6C42\uFF1B\u5DF2\u590D\u7B97 ${result.data.unchangedCount} \u5F20\u56FE\u7247\u54C8\u5E0C\u3002
31377
- ` : `\u5DF2\u751F\u6210 ${result.data.mutations.length} \u5F20\u4F18\u5316\u6D3E\u751F\u56FE\u5E76\u66F4\u65B0\u89C6\u89C9\u8BA1\u5212\uFF1B\u539F\u56FE\u672A\u88AB\u8986\u76D6\uFF0C\u8BF7\u91CD\u65B0\u751F\u6210\u9884\u89C8\u3002
31456
+ ` : `\u5DF2\u751F\u6210 ${result.data.mutations.length} \u5F20\u4F18\u5316\u6D3E\u751F\u56FE\u5E76\u66F4\u65B0\u89C6\u89C9\u8BA1\u5212\uFF1B\u672C\u6B21\u9884\u89C8\u5C06\u76F4\u63A5\u4F7F\u7528\u6D3E\u751F\u56FE\uFF0C\u539F\u56FE\u672A\u88AB\u8986\u76D6\u3002
31378
31457
  `
31379
31458
  );
31380
31459
  return result;
@@ -32177,16 +32256,32 @@ function nextStep(lastCompletedStep) {
32177
32256
  const nextIndex = lastCompletedStep === null ? 0 : CONTENT_ARTIFACT_KINDS.indexOf(lastCompletedStep) + 1;
32178
32257
  return CONTENT_ARTIFACT_KINDS[nextIndex] ?? null;
32179
32258
  }
32180
- function inputPaths(manifest, step) {
32259
+ function previousStep(step) {
32260
+ const index = CONTENT_ARTIFACT_KINDS.indexOf(step);
32261
+ return index <= 0 ? null : CONTENT_ARTIFACT_KINDS[index - 1] ?? null;
32262
+ }
32263
+ function requestedStep(value) {
32264
+ const step = value;
32265
+ if (!CONTENT_ARTIFACT_KINDS.includes(step)) {
32266
+ throw new WorkflowCliError(
32267
+ "DXC_WORKFLOW_STEP_INVALID",
32268
+ `\u672A\u77E5\u9636\u6BB5 ${step}\u3002\u8BF7\u4F7F\u7528 ${CONTENT_ARTIFACT_KINDS.join("\u3001")}\u3002`
32269
+ );
32270
+ }
32271
+ return step;
32272
+ }
32273
+ function inputPaths(directory, manifest, step) {
32181
32274
  const inputs = CONTENT_STAGE_INPUTS[step];
32182
- return [...inputs.required, ...inputs.optional].map((kind) => manifest.artifacts[kind]);
32275
+ return [...inputs.required, ...inputs.optional].map(
32276
+ (kind) => path8.join(directory, manifest.artifacts[kind])
32277
+ );
32183
32278
  }
32184
32279
  function workItem(location, state) {
32185
32280
  const step = nextStep(state.lastCompletedStep);
32186
32281
  return {
32187
32282
  done: step === null,
32188
- inputs: step === null ? [] : inputPaths(location.manifest, step),
32189
- output: step === null ? null : location.manifest.artifacts[step],
32283
+ inputs: step === null ? [] : inputPaths(location.directory, location.manifest, step),
32284
+ output: step === null ? null : path8.join(location.directory, location.manifest.artifacts[step]),
32190
32285
  project: projectSummary(location),
32191
32286
  step
32192
32287
  };
@@ -32286,7 +32381,16 @@ var WorkflowCli = class {
32286
32381
  now: this.#now
32287
32382
  });
32288
32383
  }
32289
- async #loadState(location) {
32384
+ async #locate(directory, projectId) {
32385
+ if (directory !== void 0 && projectId !== void 0) {
32386
+ throw new WorkflowCliError(
32387
+ "DXC_WORKFLOW_PROJECT_REFERENCE_INVALID",
32388
+ "\u9879\u76EE\u76EE\u5F55\u548C\u9879\u76EE ID \u53EA\u80FD\u6307\u5B9A\u4E00\u4E2A\u3002"
32389
+ );
32390
+ }
32391
+ return projectId === void 0 ? this.#projects.locate(directory) : this.#projects.locateByProjectId(projectId);
32392
+ }
32393
+ async #loadState(location, persistMigration = true, migrationUpdatedAt = this.#now().toISOString()) {
32290
32394
  try {
32291
32395
  const raw = await readPrivateFile(
32292
32396
  statePath(location.directory),
@@ -32332,36 +32436,113 @@ var WorkflowCli = class {
32332
32436
  const migrated = {
32333
32437
  lastCompletedStep,
32334
32438
  projectId: location.manifest.projectId,
32335
- updatedAt: this.#now().toISOString(),
32439
+ updatedAt: migrationUpdatedAt,
32336
32440
  version: 1
32337
32441
  };
32338
- await writePrivateJson(statePath(location.directory), migrated);
32442
+ if (persistMigration) {
32443
+ await writePrivateJson(statePath(location.directory), migrated);
32444
+ }
32339
32445
  return migrated;
32340
32446
  }
32341
32447
  async start(options) {
32342
- const directory = resolveUserPath({
32448
+ if (options.directory !== void 0 && options.projectId !== void 0) {
32449
+ throw new WorkflowCliError(
32450
+ "DXC_WORKFLOW_PROJECT_REFERENCE_INVALID",
32451
+ "\u9879\u76EE\u76EE\u5F55\u548C\u9879\u76EE ID \u53EA\u80FD\u6307\u5B9A\u4E00\u4E2A\u3002"
32452
+ );
32453
+ }
32454
+ const directory = options.directory === void 0 ? void 0 : resolveUserPath({
32343
32455
  currentDirectory: this.#currentDirectory,
32344
32456
  homeDirectory: this.#homeDirectory,
32345
32457
  input: options.directory
32346
32458
  });
32347
- const initialized = await this.#projects.initialize(directory, options.title);
32348
- const location = {
32349
- directory,
32350
- manifest: initialized.data.manifest
32351
- };
32459
+ const requested = options.from === void 0 ? void 0 : requestedStep(options.from);
32460
+ let location;
32461
+ if (options.projectId !== void 0) {
32462
+ location = await this.#projects.locateByProjectId(options.projectId);
32463
+ } else if (directory !== void 0) {
32464
+ try {
32465
+ location = await this.#projects.locate(directory);
32466
+ } catch (error) {
32467
+ if (!(error instanceof ProjectCliError) || error.code !== "DXC_PROJECT_NOT_FOUND") {
32468
+ throw error;
32469
+ }
32470
+ if (options.title === void 0 || options.title.trim().length === 0) {
32471
+ throw new WorkflowCliError(
32472
+ "DXC_WORKFLOW_TITLE_REQUIRED",
32473
+ "\u521B\u5EFA\u65B0\u9879\u76EE\u65F6\u9700\u8981\u63D0\u4F9B\u6587\u7AE0\u4E3B\u9898\u3002"
32474
+ );
32475
+ }
32476
+ const initialized = await this.#projects.initialize(directory, options.title);
32477
+ location = { directory: initialized.data.directory, manifest: initialized.data.manifest };
32478
+ }
32479
+ } else {
32480
+ if (options.title === void 0 || options.title.trim().length === 0) {
32481
+ throw new WorkflowCliError("DXC_WORKFLOW_TITLE_REQUIRED", "\u521B\u5EFA\u65B0\u9879\u76EE\u65F6\u9700\u8981\u63D0\u4F9B\u6587\u7AE0\u4E3B\u9898\u3002");
32482
+ }
32483
+ const initialized = await this.#projects.initialize(void 0, options.title);
32484
+ location = { directory: initialized.data.directory, manifest: initialized.data.manifest };
32485
+ }
32486
+ if (requested === void 0 && options.projectId !== void 0) {
32487
+ const state2 = await this.#loadState(location);
32488
+ return {
32489
+ command: "workflow.next",
32490
+ data: { match: "ready", work: workItem(location, state2) },
32491
+ ok: true
32492
+ };
32493
+ }
32494
+ if (requested === void 0 && directory !== void 0) {
32495
+ const state2 = await this.#loadState(location);
32496
+ if (state2.lastCompletedStep !== null) {
32497
+ return {
32498
+ command: "workflow.next",
32499
+ data: { match: "ready", work: workItem(location, state2) },
32500
+ ok: true
32501
+ };
32502
+ }
32503
+ }
32504
+ const step = requested ?? "research";
32352
32505
  const state = {
32353
- lastCompletedStep: null,
32506
+ lastCompletedStep: previousStep(step),
32354
32507
  projectId: location.manifest.projectId,
32355
32508
  updatedAt: this.#now().toISOString(),
32356
32509
  version: 1
32357
32510
  };
32358
- await writePrivateJson(statePath(directory), state);
32511
+ await writePrivateJson(statePath(location.directory), state);
32359
32512
  return {
32360
32513
  command: "workflow.next",
32361
32514
  data: { match: "ready", work: workItem(location, state) },
32362
32515
  ok: true
32363
32516
  };
32364
32517
  }
32518
+ async listProjects() {
32519
+ const projects = [];
32520
+ for (const indexed of await this.#projects.list()) {
32521
+ if (indexed.location === void 0) {
32522
+ projects.push({
32523
+ available: false,
32524
+ directory: indexed.entry.directory,
32525
+ done: false,
32526
+ projectId: indexed.entry.projectId,
32527
+ step: null,
32528
+ title: indexed.entry.title,
32529
+ updatedAt: indexed.entry.updatedAt
32530
+ });
32531
+ continue;
32532
+ }
32533
+ const state = await this.#loadState(indexed.location, false, indexed.entry.updatedAt);
32534
+ const step = nextStep(state.lastCompletedStep);
32535
+ projects.push({
32536
+ ...projectSummary(indexed.location),
32537
+ available: true,
32538
+ done: step === null,
32539
+ step,
32540
+ updatedAt: state.updatedAt
32541
+ });
32542
+ }
32543
+ projects.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt));
32544
+ return { command: "project.list", data: { projects }, ok: true };
32545
+ }
32365
32546
  async next(query, directory) {
32366
32547
  const projects = await this.#projects.find(query, directory);
32367
32548
  if (projects.length === 0) {
@@ -32385,8 +32566,8 @@ var WorkflowCli = class {
32385
32566
  ok: true
32386
32567
  };
32387
32568
  }
32388
- async complete(directory) {
32389
- const location = await this.#projects.locate(directory);
32569
+ async complete(directory, projectId) {
32570
+ const location = await this.#locate(directory, projectId);
32390
32571
  const state = await this.#loadState(location);
32391
32572
  const step = nextStep(state.lastCompletedStep);
32392
32573
  if (step === null) {
@@ -32416,8 +32597,8 @@ var WorkflowCli = class {
32416
32597
  ok: true
32417
32598
  };
32418
32599
  }
32419
- async deliveryInputs(directory) {
32420
- const location = await this.#projects.locate(directory);
32600
+ async deliveryInputs(directory, projectId) {
32601
+ const location = await this.#locate(directory, projectId);
32421
32602
  const state = await this.#loadState(location);
32422
32603
  if (nextStep(state.lastCompletedStep) !== "delivery") {
32423
32604
  throw new WorkflowCliError(
@@ -32548,7 +32729,7 @@ var WorkflowDeliveryCli = class {
32548
32729
  await writePrivateJson(path9.join(projectDirectory, DELIVERY_STATE_FILE), state);
32549
32730
  }
32550
32731
  async preview(options) {
32551
- const inputs = await this.#workflow.deliveryInputs(options.directory);
32732
+ const inputs = await this.#workflow.deliveryInputs(options.directory, options.projectId);
32552
32733
  const accounts = availableAccounts(await this.#wechat.accounts(this.#output));
32553
32734
  if (accounts.length === 0) {
32554
32735
  throw new WechatCliError(
@@ -32631,7 +32812,7 @@ var WorkflowDeliveryCli = class {
32631
32812
  "\u8BF7\u5148\u67E5\u770B\u5FAE\u4FE1\u9884\u89C8\u5E76\u660E\u786E\u786E\u8BA4\u521B\u5EFA\u8349\u7A3F\u3002"
32632
32813
  );
32633
32814
  }
32634
- const inputs = await this.#workflow.deliveryInputs(options.directory);
32815
+ const inputs = await this.#workflow.deliveryInputs(options.directory, options.projectId);
32635
32816
  let state = await this.#readState(inputs.projectDirectory);
32636
32817
  if (state.projectId !== inputs.project.projectId) {
32637
32818
  throw new WorkflowCliError(
@@ -32877,14 +33058,19 @@ function createProgram(context) {
32877
33058
  )
32878
33059
  );
32879
33060
  const workflow = program2.command("workflow").description("\u521B\u5EFA\u3001\u7EE7\u7EED\u548C\u4EA4\u4ED8\u6587\u7AE0");
32880
- workflow.command("start").description("\u521B\u5EFA\u4E00\u7BC7\u65B0\u6587\u7AE0\u5E76\u8FD4\u56DE\u7B2C\u4E00\u6B65").requiredOption("--title <title>", "\u6587\u7AE0\u4E3B\u9898").requiredOption("--directory <path>", "\u9879\u76EE\u76EE\u5F55").option("--json", "\u8F93\u51FA JSON").action(
33061
+ workflow.command("start").description("\u521B\u5EFA\u6216\u91CD\u65B0\u8FDB\u5165\u4E00\u7BC7\u6587\u7AE0").option("--title <title>", "\u65B0\u9879\u76EE\u7684\u6587\u7AE0\u4E3B\u9898").option("--directory <path>", "\u81EA\u5B9A\u4E49\u9879\u76EE\u76EE\u5F55").option("--from <stage>", "\u4ECE\u6307\u5B9A\u5185\u5BB9\u9636\u6BB5\u8FDB\u5165").option("--project <uuid>", "\u5DF2\u6709\u9879\u76EE ID").option("--json", "\u8F93\u51FA JSON").action(
32881
33062
  async (options) => runAction(
32882
33063
  context,
32883
33064
  options.json === true,
32884
33065
  () => new WorkflowCli({
32885
33066
  currentDirectory: context.currentDirectory,
32886
33067
  homeDirectory: context.homeDirectory
32887
- }).start({ directory: options.directory, title: options.title })
33068
+ }).start({
33069
+ ...options.directory === void 0 ? {} : { directory: options.directory },
33070
+ ...options.from === void 0 ? {} : { from: options.from },
33071
+ ...options.project === void 0 ? {} : { projectId: options.project },
33072
+ ...options.title === void 0 ? {} : { title: options.title }
33073
+ })
32888
33074
  )
32889
33075
  );
32890
33076
  workflow.command("next").description("\u8FD4\u56DE\u552F\u4E00\u9700\u8981\u6267\u884C\u7684\u4E0B\u4E00\u6B65").argument("[query]", "\u6587\u7AE0\u6807\u9898\u6216\u9879\u76EE ID").option("--directory <path>", "\u6307\u5B9A\u9879\u76EE\u76EE\u5F55").option("--json", "\u8F93\u51FA JSON").action(
@@ -32897,14 +33083,14 @@ function createProgram(context) {
32897
33083
  }).next(query, options.directory)
32898
33084
  )
32899
33085
  );
32900
- workflow.command("complete").description("\u4FDD\u5B58\u5F53\u524D\u6B65\u9AA4\u5E76\u8FD4\u56DE\u4E0B\u4E00\u6B65").option("--directory <path>", "\u9879\u76EE\u76EE\u5F55").option("--json", "\u8F93\u51FA JSON").action(
33086
+ workflow.command("complete").description("\u4FDD\u5B58\u5F53\u524D\u6B65\u9AA4\u5E76\u8FD4\u56DE\u4E0B\u4E00\u6B65").option("--directory <path>", "\u9879\u76EE\u76EE\u5F55").option("--project <uuid>", "\u9879\u76EE ID").option("--json", "\u8F93\u51FA JSON").action(
32901
33087
  async (options) => runAction(
32902
33088
  context,
32903
33089
  options.json === true,
32904
33090
  () => new WorkflowCli({
32905
33091
  currentDirectory: context.currentDirectory,
32906
33092
  homeDirectory: context.homeDirectory
32907
- }).complete(options.directory)
33093
+ }).complete(options.directory, options.project)
32908
33094
  )
32909
33095
  );
32910
33096
  const deliveryAction = async (json, action) => runAction(context, json, () => {
@@ -32920,27 +33106,40 @@ function createProgram(context) {
32920
33106
  })
32921
33107
  );
32922
33108
  });
32923
- workflow.command("preview").description("\u51C6\u5907\u5E76\u6253\u5F00\u5F53\u524D\u6587\u7AE0\u7684\u5FAE\u4FE1\u9884\u89C8").option("--account <uuid>", "\u591A\u4E2A\u516C\u4F17\u53F7\u65F6\u9009\u62E9\u76EE\u6807\u8D26\u53F7").option("--directory <path>", "\u9879\u76EE\u76EE\u5F55").option("--browser <agent|system>", "\u9884\u89C8\u6253\u5F00\u4F4D\u7F6E", "system").option("--json", "\u8F93\u51FA JSON").action(
33109
+ workflow.command("preview").description("\u51C6\u5907\u5E76\u6253\u5F00\u5F53\u524D\u6587\u7AE0\u7684\u5FAE\u4FE1\u9884\u89C8").option("--account <uuid>", "\u591A\u4E2A\u516C\u4F17\u53F7\u65F6\u9009\u62E9\u76EE\u6807\u8D26\u53F7").option("--directory <path>", "\u9879\u76EE\u76EE\u5F55").option("--project <uuid>", "\u9879\u76EE ID").option("--browser <agent|system>", "\u9884\u89C8\u6253\u5F00\u4F4D\u7F6E", "system").option("--json", "\u8F93\u51FA JSON").action(
32924
33110
  async (options) => deliveryAction(
32925
33111
  options.json === true,
32926
33112
  (delivery) => delivery.preview({
32927
33113
  ...options.account === void 0 ? {} : { accountId: options.account },
32928
33114
  browserMode: previewBrowserMode(options.browser),
32929
- ...options.directory === void 0 ? {} : { directory: options.directory }
33115
+ ...options.directory === void 0 ? {} : { directory: options.directory },
33116
+ ...options.project === void 0 ? {} : { projectId: options.project }
32930
33117
  })
32931
33118
  )
32932
33119
  );
32933
- workflow.command("deliver").description("\u786E\u8BA4\u5F53\u524D\u9884\u89C8\u5E76\u521B\u5EFA\u5FAE\u4FE1\u516C\u4F17\u53F7\u8349\u7A3F").option("--directory <path>", "\u9879\u76EE\u76EE\u5F55").option("--confirm", "\u786E\u8BA4\u5DF2\u7ECF\u67E5\u770B\u9884\u89C8\u5E76\u521B\u5EFA\u8349\u7A3F").option("--json", "\u8F93\u51FA JSON").action(
33120
+ workflow.command("deliver").description("\u786E\u8BA4\u5F53\u524D\u9884\u89C8\u5E76\u521B\u5EFA\u5FAE\u4FE1\u516C\u4F17\u53F7\u8349\u7A3F").option("--directory <path>", "\u9879\u76EE\u76EE\u5F55").option("--project <uuid>", "\u9879\u76EE ID").option("--confirm", "\u786E\u8BA4\u5DF2\u7ECF\u67E5\u770B\u9884\u89C8\u5E76\u521B\u5EFA\u8349\u7A3F").option("--json", "\u8F93\u51FA JSON").action(
32934
33121
  async (options) => deliveryAction(
32935
33122
  options.json === true,
32936
33123
  (delivery) => delivery.deliver({
32937
33124
  confirmed: options.confirm === true,
32938
- ...options.directory === void 0 ? {} : { directory: options.directory }
33125
+ ...options.directory === void 0 ? {} : { directory: options.directory },
33126
+ ...options.project === void 0 ? {} : { projectId: options.project }
32939
33127
  })
32940
33128
  )
32941
33129
  );
33130
+ const project = program2.command("project").description("\u67E5\u770B\u672C\u673A\u5185\u5BB9\u9879\u76EE");
33131
+ project.command("list").description("\u5217\u51FA\u672C\u673A\u5DF2\u767B\u8BB0\u7684\u5185\u5BB9\u9879\u76EE").option("--json", "\u8F93\u51FA JSON").action(
33132
+ async (options) => runAction(
33133
+ context,
33134
+ options.json === true,
33135
+ () => new WorkflowCli({
33136
+ currentDirectory: context.currentDirectory,
33137
+ homeDirectory: context.homeDirectory
33138
+ }).listProjects()
33139
+ )
33140
+ );
32942
33141
  const skills = program2.command("skills").description("\u5B89\u88C5 DxC \u5B98\u65B9 Skills");
32943
- skills.command("install").description("\u5B89\u88C5 5 \u4E2A\u5B98\u65B9 Skill").option("--target <codex|workbuddy>", "\u5B89\u88C5\u76EE\u6807", skillInstallTarget, "workbuddy").option("--directory <path>", "\u81EA\u5B9A\u4E49\u5B89\u88C5\u76EE\u5F55").option("--force", "\u66FF\u6362\u540C\u540D DxC Skill").option("--json", "\u8F93\u51FA JSON").action(
33142
+ skills.command("install").description("\u5B89\u88C5 14 \u4E2A\u5B98\u65B9 Skill").option("--target <codex|workbuddy>", "\u5B89\u88C5\u76EE\u6807", skillInstallTarget, "workbuddy").option("--directory <path>", "\u81EA\u5B9A\u4E49\u5B89\u88C5\u76EE\u5F55").option("--force", "\u66FF\u6362\u540C\u540D DxC Skill").option("--json", "\u8F93\u51FA JSON").action(
32944
33143
  async (options) => runAction(context, options.json === true, async () => {
32945
33144
  const result = await new SkillInstaller(
32946
33145
  context.homeDirectory,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@deployxai/dxc",
3
- "version": "0.2.0",
3
+ "version": "0.2.2",
4
4
  "description": "DeployX 内容工作台 CLI 与官方 Skills",
5
5
  "type": "module",
6
6
  "bin": {
@@ -0,0 +1,28 @@
1
+ ---
2
+ name: dxc-article-outline
3
+ description: 为 DxC 文章完成大纲阶段,把 Brief、研究材料或用户已有构思组织为可直接写作的章节结构。用户要求列大纲、重构文章结构,或 dxc-content-workflow 返回 outline 阶段时使用;可以作为八步工作流的独立起点。
4
+ ---
5
+
6
+ # DxC 文章大纲
7
+
8
+ 内部固定使用 `@deployxai/dxc@0.2.2`;Windows 通过 `npm.cmd exec`,macOS/Linux 通过 `npm exec`
9
+ 运行,不切换到 PATH 中的其他版本。
10
+
11
+ ## 进入阶段
12
+
13
+ - 总控返回 `step: outline` 时,读取 CLI 返回且真实存在的输入。
14
+ - 直接从大纲开始或重新进入已有项目时,调用:
15
+
16
+ ```text
17
+ dxc workflow start --from outline --title "<主题>" --json
18
+ ```
19
+
20
+ 已有项目改为 `--project "<项目 ID>"` 并省略 `--title`;用户在对话中给出的 Brief 或材料可以直接使用。
21
+
22
+ - 只使用本页列出的 workflow 领域命令。
23
+
24
+ ## 生成大纲
25
+
26
+ 完整读取 [references/outline-methods.md](references/outline-methods.md),选择一个最适合当前内容的主结构。
27
+ 每节写清目标、论点、证据责任和承接关系,避免为显得复杂而混搭框架。把结果写入 CLI 返回的
28
+ `output`,再调用 `dxc workflow complete --project "<项目 ID>" --json`。
@@ -0,0 +1,6 @@
1
+ interface:
2
+ display_name: "DxC 文章大纲"
3
+ short_description: "把命题和现有材料组织成可直接写作、层次清晰的文章结构"
4
+ default_prompt: "使用 $dxc-article-outline 从现有材料形成清晰、无重复的文章大纲。"
5
+ policy:
6
+ allow_implicit_invocation: false
@@ -0,0 +1,57 @@
1
+ # 大纲结构与标记
2
+
3
+ ## 专家方法来源
4
+
5
+ - 原始方法真值是开发仓库 `docs/员工BCDE的skill/employee-c-outline-architect/SKILL.md`
6
+ 的第 3–12 节;发布包不含原文,执行时以本胶囊为准。
7
+ - 本胶囊承接情绪/传播要素判定、结构选型、C→D→E 标记和自检。产物路径与进度由 CLI 统一处理。
8
+
9
+ ## 结构选择
10
+
11
+ | 内容与情绪 | 首选结构 |
12
+ | -------------------- | ------------------------------------ |
13
+ | 怕、怒、问题需要解决 | PAS:痛点、放大、方案 |
14
+ | 暖、敬、人物经历 | 目标、阻碍、努力、转折、结果 |
15
+ | 站队、观点争议 | 痛点、新观点、正例、反例、价值、行动 |
16
+ | 搞懂、认知升级 | SCQA:情境、冲突、问题、答案 |
17
+ | 方法教程 | 问题、原则、方法、案例、清单 |
18
+ | 专业论证 | 结论先行、分论点、证据 |
19
+ | 深度叙事 | 起、承、转、合 |
20
+
21
+ 只选最符合 Brief 的主结构。情绪型结构必须有出路,不能只放大焦虑。
22
+
23
+ ## 选型前的两项判定
24
+
25
+ 1. **情绪属性**:判断读者此刻是需要被理解、被说服、被解释、被推动还是被安放;情绪是
26
+ 结构张力,不等于在每段标注情绪词。
27
+ 2. **传播要素**:判断本篇最需要的是认知反差、具体利益、故事共鸣、身份认同还是可执行
28
+ 方法。它用于检查开头、核心论点和收束是否有记忆点,而不是制造标题党。
29
+
30
+ 选型后先写一句“读者从什么状态到什么状态”的结构承诺。若结构不能兑现这个变化,应换
31
+ 结构或收窄 Brief,而不是叠加多个框架。
32
+
33
+ ## 标记 contract
34
+
35
+ | 标记 | 下游动作 |
36
+ | ----------------- | ------------------------------------------------------------------------------------------------ |
37
+ | `[钩子]` | 正文前三行直接进入冲突、问题或具体场景 |
38
+ | `[情绪:<类型>]` | 类型可选痛点、放大、方案、好奇、愤怒、释然、满足、行动欲或搞懂 |
39
+ | `[金句位:<类型>]` | 类型可选共鸣、观点或反讽;正文写一句可独立成立且服务当前段落的话 |
40
+ | `[数据位:说明]` | 只补有来源的数据,缺失时保留待核实 |
41
+ | `[案例位:说明]` | 补真实、用户提供或明确脱敏的案例 |
42
+ | `[配图:<类型>]` | 类型可选思维导图、概念图、流程图、数据图、金句图、场景插画或氛围;视觉计划读取,不在正文直接插图 |
43
+
44
+ 标记必须位于相关章节内。不要把标记堆到文末。三层以上层级、概念关系、步骤、数据和金句
45
+ 分别是思维导图、概念图、流程图、数据图和金句图的信号。
46
+
47
+ ## 自检
48
+
49
+ - 大纲是否回答 Brief 的核心问题;
50
+ - 每节是否承担一个明确任务;
51
+ - 证据与案例是否有放置位置;
52
+ - 开头是否在前三行进入问题;
53
+ - 情绪放大后是否有方案;
54
+ - 金句和视觉锚点是否服务内容,而不是凑数量。
55
+ - 结构是否把读者阻碍、转折、证据和行动放在恰当顺序,而非只罗列知识点;
56
+ - 每个反例是否服务主张,结尾是否回收开头承诺;
57
+ - 标记是否让 D 能写、E 能画,而不是留下不可执行的抽象词。