facult 2.17.10 → 2.17.12

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "facult",
3
- "version": "2.17.10",
3
+ "version": "2.17.12",
4
4
  "description": "Manage canonical AI capabilities, sync surfaces, and evolution state.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/doctor.ts CHANGED
@@ -609,6 +609,22 @@ const UNRESOLVED_REFS_TEMPLATE_RE = /\$\{refs\.([A-Za-z0-9_.-]+)\}/g;
609
609
  const FCLTY_BLOCK_RE =
610
610
  /<!--\s*fclty:([^>]+?)\s*-->([\s\S]*?)<!--\s*\/fclty:\1\s*-->/g;
611
611
 
612
+ function shellQuote(value: string): string {
613
+ return `'${value.replaceAll("'", "'\\''")}'`;
614
+ }
615
+
616
+ function projectAiInitCommand(rootDir: string, flags: string[] = []): string {
617
+ const projectRoot = projectRootFromAiRoot(rootDir);
618
+ const rootFlag = projectRoot ? "--project-root" : "--root";
619
+ const rootValue = projectRoot ?? rootDir;
620
+ return [
621
+ "fclt templates init project-ai",
622
+ rootFlag,
623
+ shellQuote(rootValue),
624
+ ...flags,
625
+ ].join(" ");
626
+ }
627
+
612
628
  async function inspectCanonicalGlobalDocs(
613
629
  rootDir: string,
614
630
  opts: { projectRoot?: string | null } = {}
@@ -625,7 +641,7 @@ async function inspectCanonicalGlobalDocs(
625
641
  const text = await readFile(pathValue, "utf8");
626
642
  const issues: DoctorIssue[] = [];
627
643
  const refreshCommand = opts.projectRoot
628
- ? "fclt templates init project-ai --force"
644
+ ? projectAiInitCommand(rootDir, ["--force"])
629
645
  : "fclt templates init operating-model --global --force";
630
646
  const docLabel = opts.projectRoot
631
647
  ? "project AGENTS.global.md"
@@ -1025,7 +1041,7 @@ export async function buildDoctorReport(opts?: {
1025
1041
  message:
1026
1042
  "No canonical capability source was found in the selected .ai root.",
1027
1043
  fix: projectRoot
1028
- ? "Run fclt templates init project-ai from the repo or restore canonical project assets."
1044
+ ? `Run ${projectAiInitCommand(rootDir)} or restore canonical project assets.`
1029
1045
  : "Run fclt templates init operating-model --global.",
1030
1046
  });
1031
1047
  }
@@ -1042,7 +1058,7 @@ export async function buildDoctorReport(opts?: {
1042
1058
  ? "Refresh project operating model"
1043
1059
  : "Refresh global operating model",
1044
1060
  command: projectRoot
1045
- ? "fclt templates init project-ai --force"
1061
+ ? projectAiInitCommand(rootDir, ["--force"])
1046
1062
  : "fclt templates init operating-model --global --force",
1047
1063
  risk: "canonical_write",
1048
1064
  });
@@ -1076,7 +1092,7 @@ export async function buildDoctorReport(opts?: {
1076
1092
  actions.push({
1077
1093
  id: "init-project-ai",
1078
1094
  label: "Initialize project AI root",
1079
- command: "fclt templates init project-ai",
1095
+ command: projectAiInitCommand(rootDir),
1080
1096
  risk: "canonical_write",
1081
1097
  });
1082
1098
  }
package/src/remote.ts CHANGED
@@ -1576,14 +1576,23 @@ async function scaffoldBuiltinOperatingModelPack(args: {
1576
1576
 
1577
1577
  async function scaffoldBuiltinProjectAiPack(args: {
1578
1578
  cwd?: string;
1579
+ rootDir?: string;
1579
1580
  homeDir?: string;
1580
1581
  dryRun?: boolean;
1581
1582
  force?: boolean;
1582
1583
  update?: boolean;
1583
1584
  }): Promise<InstallResult> {
1584
1585
  const cwd = resolve(args.cwd ?? process.cwd());
1586
+ const rootDir = args.rootDir
1587
+ ? resolveCliContextRoot({
1588
+ rootArg: args.rootDir,
1589
+ scope: "project",
1590
+ cwd,
1591
+ homeDir: args.homeDir,
1592
+ })
1593
+ : join(cwd, ".ai");
1585
1594
  return await scaffoldBuiltinOperatingModelPack({
1586
- rootDir: join(cwd, ".ai"),
1595
+ rootDir,
1587
1596
  homeDir: args.homeDir,
1588
1597
  dryRun: args.dryRun,
1589
1598
  force: args.force,
@@ -3039,7 +3048,7 @@ function printTemplatesHelp() {
3039
3048
  "fclt templates init operating-model [--global|--project|--root PATH] [--update] [--force] [--dry-run]"
3040
3049
  ),
3041
3050
  renderCode(
3042
- "fclt templates init project-ai [--update] [--force] [--dry-run]"
3051
+ "fclt templates init project-ai [--project-root PATH|--root PATH] [--update] [--force] [--dry-run]"
3043
3052
  ),
3044
3053
  renderCode(
3045
3054
  "fclt templates init automation <template-id> [--scope global|project|wide] [--name <name>] [--project-root <path>] [--cwds <path1,path2>] [--rrule <RRULE>] [--status PAUSED|ACTIVE] [--yes] [--dry-run]"
@@ -3115,16 +3124,22 @@ const TEMPLATE_INIT_VALUE_FLAGS = new Set([
3115
3124
  function parseTemplateInitArgs(argv: string[]): {
3116
3125
  positional: string[];
3117
3126
  rootArg?: string;
3127
+ projectRootArg?: string;
3118
3128
  } {
3119
3129
  const positional: string[] = [];
3120
3130
  let rootArg: string | undefined;
3131
+ let projectRootArg: string | undefined;
3121
3132
  for (let i = 0; i < argv.length; i += 1) {
3122
3133
  const arg = argv[i];
3123
3134
  if (!arg) {
3124
3135
  continue;
3125
3136
  }
3126
3137
  if (arg === "--root") {
3127
- rootArg = argv[i + 1] ?? undefined;
3138
+ const value = argv[i + 1];
3139
+ if (!value || value.startsWith("-")) {
3140
+ throw new Error("--root requires a path value");
3141
+ }
3142
+ rootArg = value;
3128
3143
  i += 1;
3129
3144
  continue;
3130
3145
  }
@@ -3132,6 +3147,19 @@ function parseTemplateInitArgs(argv: string[]): {
3132
3147
  rootArg = arg.slice("--root=".length);
3133
3148
  continue;
3134
3149
  }
3150
+ if (arg === "--project-root") {
3151
+ const value = argv[i + 1];
3152
+ if (!value || value.startsWith("-")) {
3153
+ throw new Error("--project-root requires a path value");
3154
+ }
3155
+ projectRootArg = value;
3156
+ i += 1;
3157
+ continue;
3158
+ }
3159
+ if (arg.startsWith("--project-root=")) {
3160
+ projectRootArg = arg.slice("--project-root=".length);
3161
+ continue;
3162
+ }
3135
3163
  if (TEMPLATE_INIT_VALUE_FLAGS.has(arg)) {
3136
3164
  i += 1;
3137
3165
  continue;
@@ -3145,7 +3173,22 @@ function parseTemplateInitArgs(argv: string[]): {
3145
3173
  }
3146
3174
  positional.push(arg);
3147
3175
  }
3148
- return { positional, rootArg };
3176
+ return { positional, rootArg, projectRootArg };
3177
+ }
3178
+
3179
+ function expandHomePath(pathValue: string, homeDir: string): string {
3180
+ if (pathValue === "~") {
3181
+ return homeDir;
3182
+ }
3183
+ if (pathValue.startsWith("~/")) {
3184
+ return `${homeDir}/${pathValue.slice(2)}`;
3185
+ }
3186
+ return pathValue;
3187
+ }
3188
+
3189
+ function projectAiRootFromProjectArg(value: string, homeDir?: string): string {
3190
+ const resolved = resolve(expandHomePath(value, homeDir ?? homedir()));
3191
+ return basename(resolved) === ".ai" ? resolved : join(resolved, ".ai");
3149
3192
  }
3150
3193
 
3151
3194
  export async function sourcesCommand(
@@ -3543,6 +3586,10 @@ export async function templatesCommand(
3543
3586
  process.exitCode = 2;
3544
3587
  return;
3545
3588
  }
3589
+ if (args.includes("--help") || args.includes("-h") || args[0] === "help") {
3590
+ printTemplatesHelp();
3591
+ return;
3592
+ }
3546
3593
  const dryRun = args.includes("--dry-run");
3547
3594
  const force = args.includes("--force");
3548
3595
  const update = args.includes("--update");
@@ -3554,6 +3601,15 @@ export async function templatesCommand(
3554
3601
  try {
3555
3602
  const result = await scaffoldBuiltinProjectAiPack({
3556
3603
  cwd: ctx.cwd,
3604
+ rootDir:
3605
+ parsedArgs.rootArg ??
3606
+ ctx.rootDir ??
3607
+ (parsedArgs.projectRootArg
3608
+ ? projectAiRootFromProjectArg(
3609
+ parsedArgs.projectRootArg,
3610
+ ctx.homeDir
3611
+ )
3612
+ : undefined),
3557
3613
  homeDir: ctx.homeDir,
3558
3614
  dryRun,
3559
3615
  force,