@agentic-kit/pi 0.2.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.
Files changed (115) hide show
  1. package/LICENSE +23 -0
  2. package/README.md +126 -0
  3. package/app-workspace.d.ts +16 -0
  4. package/app-workspace.js +145 -0
  5. package/confirm-gate.d.ts +13 -0
  6. package/confirm-gate.js +51 -0
  7. package/context.d.ts +39 -0
  8. package/context.js +251 -0
  9. package/db-probe.d.ts +29 -0
  10. package/db-probe.js +50 -0
  11. package/esm/app-workspace.d.ts +16 -0
  12. package/esm/app-workspace.js +134 -0
  13. package/esm/confirm-gate.d.ts +13 -0
  14. package/esm/confirm-gate.js +48 -0
  15. package/esm/context.d.ts +39 -0
  16. package/esm/context.js +242 -0
  17. package/esm/db-probe.d.ts +29 -0
  18. package/esm/db-probe.js +47 -0
  19. package/esm/host.d.ts +55 -0
  20. package/esm/host.js +20 -0
  21. package/esm/index.d.ts +11 -0
  22. package/esm/index.js +48 -0
  23. package/esm/policy/add-policies-to-table.d.ts +26 -0
  24. package/esm/policy/add-policies-to-table.js +66 -0
  25. package/esm/policy/provision-helpers.d.ts +38 -0
  26. package/esm/policy/provision-helpers.js +50 -0
  27. package/esm/provision-database/create-database-provision.d.ts +11 -0
  28. package/esm/provision-database/create-database-provision.js +48 -0
  29. package/esm/provision-database/credential.d.ts +14 -0
  30. package/esm/provision-database/credential.js +19 -0
  31. package/esm/provision-database/env-file.d.ts +37 -0
  32. package/esm/provision-database/env-file.js +85 -0
  33. package/esm/provision-database/modules.d.ts +2 -0
  34. package/esm/provision-database/modules.js +40 -0
  35. package/esm/provision-database/pg-fixups.d.ts +16 -0
  36. package/esm/provision-database/pg-fixups.js +132 -0
  37. package/esm/records/meta.d.ts +41 -0
  38. package/esm/records/meta.js +170 -0
  39. package/esm/run-codegen/barrels.d.ts +3 -0
  40. package/esm/run-codegen/barrels.js +48 -0
  41. package/esm/run-codegen/endpoints.d.ts +8 -0
  42. package/esm/run-codegen/endpoints.js +46 -0
  43. package/esm/schema-resolve.d.ts +23 -0
  44. package/esm/schema-resolve.js +69 -0
  45. package/esm/tool-schema.d.ts +3 -0
  46. package/esm/tool-schema.js +10 -0
  47. package/esm/tools/add-policies.d.ts +8 -0
  48. package/esm/tools/add-policies.js +96 -0
  49. package/esm/tools/add-records.d.ts +14 -0
  50. package/esm/tools/add-records.js +124 -0
  51. package/esm/tools/add-relation-schema.d.ts +30 -0
  52. package/esm/tools/add-relation-schema.js +58 -0
  53. package/esm/tools/add-relation.d.ts +10 -0
  54. package/esm/tools/add-relation.js +91 -0
  55. package/esm/tools/describe-schema.d.ts +18 -0
  56. package/esm/tools/describe-schema.js +132 -0
  57. package/esm/tools/mutations.d.ts +14 -0
  58. package/esm/tools/mutations.js +235 -0
  59. package/esm/tools/provision-blueprint.d.ts +22 -0
  60. package/esm/tools/provision-blueprint.js +111 -0
  61. package/esm/tools/provision-database.d.ts +14 -0
  62. package/esm/tools/provision-database.js +251 -0
  63. package/esm/tools/run-codegen.d.ts +11 -0
  64. package/esm/tools/run-codegen.js +139 -0
  65. package/esm/tools/templates.d.ts +49 -0
  66. package/esm/tools/templates.js +337 -0
  67. package/host.d.ts +55 -0
  68. package/host.js +25 -0
  69. package/index.d.ts +11 -0
  70. package/index.js +60 -0
  71. package/package.json +51 -0
  72. package/policy/add-policies-to-table.d.ts +26 -0
  73. package/policy/add-policies-to-table.js +71 -0
  74. package/policy/provision-helpers.d.ts +38 -0
  75. package/policy/provision-helpers.js +58 -0
  76. package/provision-database/create-database-provision.d.ts +11 -0
  77. package/provision-database/create-database-provision.js +51 -0
  78. package/provision-database/credential.d.ts +14 -0
  79. package/provision-database/credential.js +22 -0
  80. package/provision-database/env-file.d.ts +37 -0
  81. package/provision-database/env-file.js +91 -0
  82. package/provision-database/modules.d.ts +2 -0
  83. package/provision-database/modules.js +43 -0
  84. package/provision-database/pg-fixups.d.ts +16 -0
  85. package/provision-database/pg-fixups.js +170 -0
  86. package/records/meta.d.ts +41 -0
  87. package/records/meta.js +175 -0
  88. package/run-codegen/barrels.d.ts +3 -0
  89. package/run-codegen/barrels.js +54 -0
  90. package/run-codegen/endpoints.d.ts +8 -0
  91. package/run-codegen/endpoints.js +51 -0
  92. package/schema-resolve.d.ts +23 -0
  93. package/schema-resolve.js +75 -0
  94. package/tool-schema.d.ts +3 -0
  95. package/tool-schema.js +13 -0
  96. package/tools/add-policies.d.ts +8 -0
  97. package/tools/add-policies.js +99 -0
  98. package/tools/add-records.d.ts +14 -0
  99. package/tools/add-records.js +127 -0
  100. package/tools/add-relation-schema.d.ts +30 -0
  101. package/tools/add-relation-schema.js +62 -0
  102. package/tools/add-relation.d.ts +10 -0
  103. package/tools/add-relation.js +94 -0
  104. package/tools/describe-schema.d.ts +18 -0
  105. package/tools/describe-schema.js +135 -0
  106. package/tools/mutations.d.ts +14 -0
  107. package/tools/mutations.js +238 -0
  108. package/tools/provision-blueprint.d.ts +22 -0
  109. package/tools/provision-blueprint.js +114 -0
  110. package/tools/provision-database.d.ts +14 -0
  111. package/tools/provision-database.js +257 -0
  112. package/tools/run-codegen.d.ts +11 -0
  113. package/tools/run-codegen.js +143 -0
  114. package/tools/templates.d.ts +49 -0
  115. package/tools/templates.js +341 -0
@@ -0,0 +1,30 @@
1
+ import type { BlueprintDefinition } from '@agentic-kit/harness';
2
+ import { z } from 'zod';
3
+ export declare const DeleteAction: z.ZodEnum<{
4
+ c: "c";
5
+ r: "r";
6
+ n: "n";
7
+ a: "a";
8
+ d: "d";
9
+ }>;
10
+ export declare const AddRelationZod: z.ZodObject<{
11
+ relation_type: z.ZodOptional<z.ZodEnum<{
12
+ belongs_to: "belongs_to";
13
+ many_to_many: "many_to_many";
14
+ }>>;
15
+ source_table: z.ZodString;
16
+ target_table: z.ZodString;
17
+ field_name: z.ZodOptional<z.ZodString>;
18
+ junction_table_name: z.ZodOptional<z.ZodString>;
19
+ delete_action: z.ZodOptional<z.ZodEnum<{
20
+ c: "c";
21
+ r: "r";
22
+ n: "n";
23
+ a: "a";
24
+ d: "d";
25
+ }>>;
26
+ is_required: z.ZodOptional<z.ZodBoolean>;
27
+ }, z.core.$strip>;
28
+ export declare const AddRelationSchema: import("typebox/type").TSchema;
29
+ export type AddRelationParams = z.infer<typeof AddRelationZod>;
30
+ export declare function buildRelation(params: AddRelationParams): BlueprintDefinition['relations'];
@@ -0,0 +1,62 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.AddRelationSchema = exports.AddRelationZod = exports.DeleteAction = void 0;
4
+ exports.buildRelation = buildRelation;
5
+ const zod_1 = require("zod");
6
+ const tool_schema_1 = require("../tool-schema");
7
+ // delete_action is intentionally always sent: the backend REJECTS a
8
+ // RelationBelongsTo whose delete_action is absent ("PROVISION_RELATION:
9
+ // delete_action is required"). We default it to 'a' (NO ACTION) so the model
10
+ // never has to supply it.
11
+ exports.DeleteAction = zod_1.z
12
+ .enum(['c', 'r', 'n', 'a', 'd'])
13
+ .describe('FK delete action: c=CASCADE, r=RESTRICT, n=SET NULL, a=NO ACTION (default), d=SET DEFAULT');
14
+ exports.AddRelationZod = zod_1.z.object({
15
+ relation_type: zod_1.z
16
+ .enum(['belongs_to', 'many_to_many'])
17
+ .describe('belongs_to adds an FK column on the source table; many_to_many adds a junction table. Defaults to belongs_to.')
18
+ .optional(),
19
+ source_table: zod_1.z.string().describe('Existing source table name (snake_case).'),
20
+ target_table: zod_1.z.string().describe('Existing target table name (snake_case).'),
21
+ field_name: zod_1.z
22
+ .string()
23
+ .describe('belongs_to only: FK column name to add on the source table, e.g. "author_id".')
24
+ .optional(),
25
+ junction_table_name: zod_1.z
26
+ .string()
27
+ .describe('many_to_many only: name of the junction table to create, e.g. "post_tags".')
28
+ .optional(),
29
+ delete_action: exports.DeleteAction.optional(),
30
+ is_required: zod_1.z
31
+ .boolean()
32
+ .describe('belongs_to only: make the FK NOT NULL. Defaults to false — a NOT NULL FK fails if the source table already has rows.')
33
+ .optional(),
34
+ });
35
+ exports.AddRelationSchema = (0, tool_schema_1.toolSchema)(exports.AddRelationZod);
36
+ // Build a relation-only blueprint definition (no tables): the spike proved
37
+ // constructBlueprint applies relations to PRE-EXISTING tables without recreating
38
+ // them, as long as delete_action is supplied.
39
+ function buildRelation(params) {
40
+ const deleteAction = params.delete_action ?? 'a';
41
+ if ((params.relation_type ?? 'belongs_to') === 'many_to_many') {
42
+ return [
43
+ {
44
+ $type: 'RelationManyToMany',
45
+ source_table: params.source_table,
46
+ target_table: params.target_table,
47
+ junction_table_name: params.junction_table_name,
48
+ delete_action: deleteAction,
49
+ },
50
+ ];
51
+ }
52
+ return [
53
+ {
54
+ $type: 'RelationBelongsTo',
55
+ source_table: params.source_table,
56
+ target_table: params.target_table,
57
+ field_name: params.field_name,
58
+ delete_action: deleteAction,
59
+ is_required: params.is_required ?? false,
60
+ },
61
+ ];
62
+ }
@@ -0,0 +1,10 @@
1
+ import type { ToolDefinition } from '@earendil-works/pi-coding-agent';
2
+ import { AddRelationSchema } from './add-relation-schema';
3
+ export type AddRelationDetails = {
4
+ success: boolean;
5
+ message: string;
6
+ sourceTable?: string;
7
+ targetTable?: string;
8
+ fieldName?: string;
9
+ };
10
+ export declare const addRelationTool: ToolDefinition<typeof AddRelationSchema, AddRelationDetails>;
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.addRelationTool = void 0;
4
+ const harness_1 = require("@agentic-kit/harness");
5
+ const context_1 = require("../context");
6
+ const schema_resolve_1 = require("../schema-resolve");
7
+ const add_relation_schema_1 = require("./add-relation-schema");
8
+ function fail(message) {
9
+ return { content: [{ type: 'text', text: message }], details: { success: false, message } };
10
+ }
11
+ exports.addRelationTool = {
12
+ name: 'add_relation',
13
+ label: 'Add relation',
14
+ description: 'Add a relation between two EXISTING tables without recreating them: belongs_to adds a foreign-key column on the source table; many_to_many creates a junction table. Use this for relations discovered after the tables were provisioned. For brand-new related tables, prefer a single provision_blueprint call.',
15
+ promptSnippet: 'add_relation: link two existing tables (belongs_to FK column, or many_to_many junction). Gated.',
16
+ parameters: add_relation_schema_1.AddRelationSchema,
17
+ async execute(_id, params, _signal, _onUpdate, ctx) {
18
+ const type = params.relation_type ?? 'belongs_to';
19
+ if (type === 'belongs_to' && !params.field_name) {
20
+ return fail('field_name is required for a belongs_to relation (the FK column name on the source table).');
21
+ }
22
+ if (type === 'many_to_many' && !params.junction_table_name) {
23
+ return fail('junction_table_name is required for a many_to_many relation.');
24
+ }
25
+ const resolved = await (0, context_1.resolveProjectContext)(ctx.cwd);
26
+ if (!resolved.context)
27
+ return fail(resolved.reason);
28
+ const { modules, databaseId, schemaId, ownerId } = resolved.context;
29
+ if (!ownerId)
30
+ return fail('Not authenticated (missing OWNER_ID in .env).');
31
+ // Validate both tables exist before constructing — a clearer error than the
32
+ // backend's, and avoids a no-op blueprint.
33
+ let schema;
34
+ try {
35
+ schema = await (0, schema_resolve_1.resolveSchema)(resolved.context);
36
+ (0, schema_resolve_1.resolveTable)(schema, params.source_table);
37
+ (0, schema_resolve_1.resolveTable)(schema, params.target_table);
38
+ }
39
+ catch (err) {
40
+ return fail(err instanceof Error ? err.message : 'Failed to resolve schema.');
41
+ }
42
+ const definition = (0, harness_1.expandBlueprintDefaults)({ tables: [], relations: (0, add_relation_schema_1.buildRelation)(params) });
43
+ const uniqueName = `add_relation_${Date.now()}`;
44
+ try {
45
+ const created = await modules.blueprint
46
+ .create({
47
+ data: {
48
+ ownerId,
49
+ databaseId,
50
+ name: uniqueName,
51
+ displayName: `${params.source_table} → ${params.target_table}`,
52
+ description: `Add ${type} relation`,
53
+ definition,
54
+ },
55
+ select: { id: true },
56
+ })
57
+ .unwrap();
58
+ const blueprintId = created.createBlueprint?.blueprint?.id;
59
+ if (!blueprintId)
60
+ throw new Error('Failed to create relation blueprint');
61
+ const constructResult = await modules.mutation
62
+ .constructBlueprint({ input: { blueprintId, schemaId } }, { select: { result: true } })
63
+ .unwrap();
64
+ if (!constructResult.constructBlueprint?.result) {
65
+ const constructions = await modules.blueprintConstruction
66
+ .findMany({
67
+ where: { blueprintId: { equalTo: blueprintId } },
68
+ orderBy: ['ID_DESC'],
69
+ first: 1,
70
+ select: { status: true, errorDetails: true },
71
+ })
72
+ .unwrap();
73
+ const error = constructions?.blueprintConstructions?.nodes?.[0]?.errorDetails ?? 'Unknown error';
74
+ return fail(`Failed to add relation: ${typeof error === 'string' ? error : JSON.stringify(error)}`);
75
+ }
76
+ }
77
+ catch (err) {
78
+ return fail(`Failed to add relation: ${err instanceof Error ? err.message : String(err)}`);
79
+ }
80
+ const message = type === 'many_to_many'
81
+ ? `Created junction table "${params.junction_table_name}" linking ${params.source_table} ↔ ${params.target_table}.`
82
+ : `Added FK "${params.field_name}" on ${params.source_table} → ${params.target_table}.`;
83
+ return {
84
+ content: [{ type: 'text', text: message }],
85
+ details: {
86
+ success: true,
87
+ message,
88
+ sourceTable: params.source_table,
89
+ targetTable: params.target_table,
90
+ fieldName: params.field_name,
91
+ },
92
+ };
93
+ },
94
+ };
@@ -0,0 +1,18 @@
1
+ import type { ToolDefinition } from '@earendil-works/pi-coding-agent';
2
+ declare const ParamSchema: import("typebox/type").TSchema;
3
+ type SchemaField = {
4
+ name: string;
5
+ type: string;
6
+ isRequired: boolean;
7
+ defaultValue: string | null;
8
+ };
9
+ type SchemaTable = {
10
+ name: string;
11
+ fields: SchemaField[];
12
+ policies: string[];
13
+ };
14
+ export type DescribeSchemaDetails = {
15
+ tables: SchemaTable[];
16
+ };
17
+ export declare const describeSchemaTool: ToolDefinition<typeof ParamSchema, DescribeSchemaDetails>;
18
+ export {};
@@ -0,0 +1,135 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.describeSchemaTool = void 0;
4
+ const harness_1 = require("@agentic-kit/harness");
5
+ const zod_1 = require("zod");
6
+ const context_1 = require("../context");
7
+ const tool_schema_1 = require("../tool-schema");
8
+ const ParamZod = zod_1.z.object({});
9
+ const ParamSchema = (0, tool_schema_1.toolSchema)(ParamZod);
10
+ function toDefaultValueString(value) {
11
+ if (value == null)
12
+ return null;
13
+ if (typeof value === 'string')
14
+ return value;
15
+ return JSON.stringify(value);
16
+ }
17
+ function formatText(details) {
18
+ const lines = [];
19
+ if (details.tables.length === 0) {
20
+ lines.push('No application tables yet.');
21
+ }
22
+ else {
23
+ lines.push(`Tables (${details.tables.length}):`);
24
+ for (const table of details.tables) {
25
+ lines.push(` ${table.name}`);
26
+ for (const field of table.fields) {
27
+ const req = field.isRequired ? ' required' : '';
28
+ const def = field.defaultValue != null ? ` default=${field.defaultValue}` : '';
29
+ lines.push(` - ${field.name}: ${field.type}${req}${def}`);
30
+ }
31
+ if (table.policies.length > 0) {
32
+ lines.push(` policies: ${table.policies.join(', ')}`);
33
+ }
34
+ }
35
+ }
36
+ return lines.join('\n');
37
+ }
38
+ exports.describeSchemaTool = {
39
+ name: 'describe_schema',
40
+ label: 'Describe schema',
41
+ description: 'Inspect the current Constructive database schema for this project: application tables with their fields (name, type, required, default). Read-only.',
42
+ promptSnippet: 'describe_schema: list the project database tables and their fields. Read-only — call before modifying schema.',
43
+ parameters: ParamSchema,
44
+ async execute(_toolCallId, _params, _signal, _onUpdate, ctx) {
45
+ const resolved = await (0, context_1.resolveProjectContext)(ctx.cwd);
46
+ if (!resolved.context) {
47
+ return {
48
+ content: [{ type: 'text', text: resolved.reason }],
49
+ details: { tables: [] },
50
+ };
51
+ }
52
+ const { api, databaseId } = resolved.context;
53
+ const tablesResult = await api.table
54
+ .findMany({
55
+ select: { id: true, name: true, category: true },
56
+ where: {
57
+ databaseId: { equalTo: databaseId },
58
+ or: [{ category: { equalTo: 'APP' } }, { name: { equalTo: 'users' } }],
59
+ },
60
+ orderBy: ['NAME_ASC'],
61
+ })
62
+ .execute();
63
+ if (!tablesResult.ok) {
64
+ const message = tablesResult.errors.map((e) => e.message).join('; ');
65
+ return {
66
+ content: [{ type: 'text', text: `Failed to read schema: ${message}` }],
67
+ details: { tables: [] },
68
+ };
69
+ }
70
+ const tableNodes = tablesResult.data.tables?.nodes ?? [];
71
+ const tableIds = tableNodes.map((t) => t.id);
72
+ const fieldsByTable = new Map();
73
+ const policiesByTable = new Map();
74
+ if (tableIds.length > 0) {
75
+ const [fieldsResult, policiesResult] = await Promise.all([
76
+ api.field
77
+ .findMany({
78
+ select: {
79
+ name: true,
80
+ type: true,
81
+ isRequired: true,
82
+ defaultValue: true,
83
+ tableId: true,
84
+ },
85
+ where: { tableId: { in: tableIds } },
86
+ })
87
+ .execute(),
88
+ api.policy
89
+ .findMany({
90
+ select: { tableId: true, policyType: true },
91
+ where: { tableId: { in: tableIds } },
92
+ })
93
+ .execute(),
94
+ ]);
95
+ if (!fieldsResult.ok) {
96
+ const message = fieldsResult.errors.map((e) => e.message).join('; ');
97
+ return {
98
+ content: [{ type: 'text', text: `Failed to read fields: ${message}` }],
99
+ details: { tables: [] },
100
+ };
101
+ }
102
+ for (const field of fieldsResult.data.fields?.nodes ?? []) {
103
+ const list = fieldsByTable.get(field.tableId) ?? [];
104
+ list.push({
105
+ name: field.name,
106
+ type: (0, harness_1.fieldTypeToTypeName)(field.type),
107
+ isRequired: Boolean(field.isRequired),
108
+ defaultValue: toDefaultValueString(field.defaultValue),
109
+ });
110
+ fieldsByTable.set(field.tableId, list);
111
+ }
112
+ // One policy spans CRUD as several rows; collapse to distinct types per table.
113
+ if (policiesResult.ok) {
114
+ for (const policy of policiesResult.data.policies?.nodes ?? []) {
115
+ if ((0, harness_1.isInternalPolicy)(policy.policyType))
116
+ continue;
117
+ const list = policiesByTable.get(policy.tableId) ?? [];
118
+ if (!list.includes(policy.policyType))
119
+ list.push(policy.policyType);
120
+ policiesByTable.set(policy.tableId, list);
121
+ }
122
+ }
123
+ }
124
+ const tables = tableNodes.map((table) => ({
125
+ name: table.name,
126
+ fields: fieldsByTable.get(table.id) ?? [],
127
+ policies: policiesByTable.get(table.id) ?? [],
128
+ }));
129
+ const details = { tables };
130
+ return {
131
+ content: [{ type: 'text', text: formatText(details) }],
132
+ details,
133
+ };
134
+ },
135
+ };
@@ -0,0 +1,14 @@
1
+ import type { ToolDefinition } from '@earendil-works/pi-coding-agent';
2
+ export type MutationDetails = {
3
+ success: boolean;
4
+ message: string;
5
+ };
6
+ declare const DeleteTableSchema: import("typebox/type").TSchema;
7
+ export declare const deleteTableTool: ToolDefinition<typeof DeleteTableSchema, MutationDetails>;
8
+ declare const CreateFieldSchema: import("typebox/type").TSchema;
9
+ export declare const createFieldTool: ToolDefinition<typeof CreateFieldSchema, MutationDetails>;
10
+ declare const UpdateFieldSchema: import("typebox/type").TSchema;
11
+ export declare const updateFieldTool: ToolDefinition<typeof UpdateFieldSchema, MutationDetails>;
12
+ declare const DeleteFieldSchema: import("typebox/type").TSchema;
13
+ export declare const deleteFieldTool: ToolDefinition<typeof DeleteFieldSchema, MutationDetails>;
14
+ export {};
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.deleteFieldTool = exports.updateFieldTool = exports.createFieldTool = exports.deleteTableTool = void 0;
4
+ const harness_1 = require("@agentic-kit/harness");
5
+ const zod_1 = require("zod");
6
+ const context_1 = require("../context");
7
+ const schema_resolve_1 = require("../schema-resolve");
8
+ const tool_schema_1 = require("../tool-schema");
9
+ function ok(message) {
10
+ return {
11
+ content: [{ type: 'text', text: message }],
12
+ details: { success: true, message },
13
+ };
14
+ }
15
+ function fail(message) {
16
+ return {
17
+ content: [{ type: 'text', text: message }],
18
+ details: { success: false, message },
19
+ };
20
+ }
21
+ // ── delete_table ──────────────────────────────────────────────────────────
22
+ const DeleteTableZod = zod_1.z.object({
23
+ table_name: zod_1.z.string().describe('Name of the table to delete (snake_case)'),
24
+ });
25
+ const DeleteTableSchema = (0, tool_schema_1.toolSchema)(DeleteTableZod);
26
+ exports.deleteTableTool = {
27
+ name: 'delete_table',
28
+ label: 'Delete table',
29
+ description: 'Permanently delete a table and its data from the project database.',
30
+ promptSnippet: 'delete_table: drop an existing table by name. Destructive — gated.',
31
+ parameters: DeleteTableSchema,
32
+ async execute(_id, params, _signal, _onUpdate, ctx) {
33
+ const resolved = await (0, context_1.resolveProjectContext)(ctx.cwd);
34
+ if (!resolved.context)
35
+ return fail(resolved.reason);
36
+ try {
37
+ const schema = await (0, schema_resolve_1.resolveSchema)(resolved.context);
38
+ const table = (0, schema_resolve_1.resolveTable)(schema, params.table_name);
39
+ await resolved.context.api.table
40
+ .delete({ where: { id: table.id }, select: { id: true } })
41
+ .unwrap();
42
+ return ok(`Deleted table ${params.table_name}`);
43
+ }
44
+ catch (err) {
45
+ return fail(err instanceof Error ? err.message : 'Failed to delete table');
46
+ }
47
+ },
48
+ };
49
+ // ── create_field ──────────────────────────────────────────────────────────
50
+ const CreateFieldZod = zod_1.z.object({
51
+ table_name: zod_1.z.string().describe('Name of the table to add the field to'),
52
+ field_name: zod_1.z.string().describe('Field name in snake_case'),
53
+ type: zod_1.z
54
+ .string()
55
+ .describe('PostgreSQL type: text, integer, smallint, numeric, boolean, date, timestamp, timestamptz, time, json, jsonb, inet, email, url, image, upload'),
56
+ is_required: zod_1.z.boolean().describe('Whether the field is NOT NULL. Defaults to false.').optional(),
57
+ default_value: zod_1.z
58
+ .union([zod_1.z.string(), zod_1.z.null()])
59
+ .describe('SQL default expression, e.g. "now()", "\'draft\'"')
60
+ .optional(),
61
+ is_unique: zod_1.z
62
+ .boolean()
63
+ .describe('Whether the field has a unique constraint. Defaults to false.')
64
+ .optional(),
65
+ });
66
+ const CreateFieldSchema = (0, tool_schema_1.toolSchema)(CreateFieldZod);
67
+ exports.createFieldTool = {
68
+ name: 'create_field',
69
+ label: 'Create field',
70
+ description: 'Add a new field (column) to an existing table in the project database.',
71
+ promptSnippet: 'create_field: add a column to an existing table. Gated.',
72
+ parameters: CreateFieldSchema,
73
+ async execute(_id, params, _signal, _onUpdate, ctx) {
74
+ const resolved = await (0, context_1.resolveProjectContext)(ctx.cwd);
75
+ if (!resolved.context)
76
+ return fail(resolved.reason);
77
+ const { api, databaseId } = resolved.context;
78
+ try {
79
+ const schema = await (0, schema_resolve_1.resolveSchema)(resolved.context);
80
+ const table = (0, schema_resolve_1.resolveTable)(schema, params.table_name);
81
+ const result = await api.field
82
+ .create({
83
+ data: {
84
+ name: params.field_name,
85
+ type: (0, harness_1.toFieldType)(params.type),
86
+ tableId: table.id,
87
+ databaseId,
88
+ isRequired: params.is_required ?? false,
89
+ defaultValue: (0, harness_1.toFieldDefault)(params.default_value),
90
+ },
91
+ select: { id: true, name: true, type: true },
92
+ })
93
+ .unwrap();
94
+ const createdId = result.createField?.field?.id;
95
+ if (params.is_unique && createdId) {
96
+ try {
97
+ await api.uniqueConstraint
98
+ .create({
99
+ data: {
100
+ tableId: table.id,
101
+ databaseId,
102
+ fieldIds: [createdId],
103
+ name: `${table.name}_${params.field_name}_key`,
104
+ type: 'u',
105
+ },
106
+ select: { id: true },
107
+ })
108
+ .unwrap();
109
+ }
110
+ catch (err) {
111
+ console.warn('Failed to create unique constraint:', err);
112
+ }
113
+ }
114
+ return ok(`Added field ${params.table_name}.${params.field_name}`);
115
+ }
116
+ catch (err) {
117
+ return fail(err instanceof Error ? err.message : 'Failed to create field');
118
+ }
119
+ },
120
+ };
121
+ // ── update_field ──────────────────────────────────────────────────────────
122
+ const UpdateFieldZod = zod_1.z.object({
123
+ table_name: zod_1.z.string().describe('Name of the table containing the field'),
124
+ field_name: zod_1.z.string().describe('Current field name'),
125
+ new_name: zod_1.z.string().describe('New field name in snake_case (if renaming)').optional(),
126
+ new_type: zod_1.z.string().describe('New PostgreSQL type (if changing type)').optional(),
127
+ is_required: zod_1.z.boolean().describe('Set NOT NULL constraint').optional(),
128
+ default_value: zod_1.z
129
+ .union([zod_1.z.string(), zod_1.z.null()])
130
+ .describe('New SQL default expression, or null to remove')
131
+ .optional(),
132
+ is_unique: zod_1.z.boolean().describe('Set or remove unique constraint').optional(),
133
+ });
134
+ const UpdateFieldSchema = (0, tool_schema_1.toolSchema)(UpdateFieldZod);
135
+ exports.updateFieldTool = {
136
+ name: 'update_field',
137
+ label: 'Update field',
138
+ description: 'Modify an existing field: rename, change type, toggle required/unique, or set default.',
139
+ promptSnippet: 'update_field: rename/retype/toggle an existing column. Gated.',
140
+ parameters: UpdateFieldSchema,
141
+ async execute(_id, params, _signal, _onUpdate, ctx) {
142
+ const resolved = await (0, context_1.resolveProjectContext)(ctx.cwd);
143
+ if (!resolved.context)
144
+ return fail(resolved.reason);
145
+ const { api, databaseId } = resolved.context;
146
+ try {
147
+ const schema = await (0, schema_resolve_1.resolveSchema)(resolved.context);
148
+ const table = (0, schema_resolve_1.resolveTable)(schema, params.table_name);
149
+ const field = (0, schema_resolve_1.resolveField)(table, params.field_name);
150
+ const patch = {};
151
+ if (params.new_name)
152
+ patch.name = params.new_name;
153
+ if (params.new_type)
154
+ patch.type = (0, harness_1.toFieldType)(params.new_type);
155
+ if (params.is_required !== undefined)
156
+ patch.isRequired = params.is_required;
157
+ if (params.default_value !== undefined) {
158
+ patch.defaultValue =
159
+ params.default_value === null ? null : ((0, harness_1.toFieldDefault)(params.default_value) ?? null);
160
+ }
161
+ if (Object.keys(patch).length > 0) {
162
+ await api.field
163
+ .update({
164
+ where: { id: field.id },
165
+ data: patch,
166
+ select: { id: true, name: true, type: true },
167
+ })
168
+ .unwrap();
169
+ }
170
+ if (params.is_unique !== undefined) {
171
+ const existingUniqueId = (0, schema_resolve_1.findUniqueConstraintId)(table, field.id);
172
+ if (params.is_unique && !existingUniqueId) {
173
+ try {
174
+ await api.uniqueConstraint
175
+ .create({
176
+ data: {
177
+ tableId: table.id,
178
+ databaseId,
179
+ fieldIds: [field.id],
180
+ name: `${table.name}_${params.new_name ?? field.name}_key`,
181
+ type: 'u',
182
+ },
183
+ select: { id: true },
184
+ })
185
+ .unwrap();
186
+ }
187
+ catch (err) {
188
+ console.warn('Failed to create unique constraint:', err);
189
+ }
190
+ }
191
+ else if (!params.is_unique && existingUniqueId) {
192
+ try {
193
+ await api.uniqueConstraint
194
+ .delete({ where: { id: existingUniqueId }, select: { id: true } })
195
+ .unwrap();
196
+ }
197
+ catch (err) {
198
+ console.warn('Failed to delete unique constraint:', err);
199
+ }
200
+ }
201
+ }
202
+ return ok(`Updated field ${params.table_name}.${params.field_name}`);
203
+ }
204
+ catch (err) {
205
+ return fail(err instanceof Error ? err.message : 'Failed to update field');
206
+ }
207
+ },
208
+ };
209
+ // ── delete_field ──────────────────────────────────────────────────────────
210
+ const DeleteFieldZod = zod_1.z.object({
211
+ table_name: zod_1.z.string().describe('Name of the table containing the field'),
212
+ field_name: zod_1.z.string().describe('Name of the field to delete'),
213
+ });
214
+ const DeleteFieldSchema = (0, tool_schema_1.toolSchema)(DeleteFieldZod);
215
+ exports.deleteFieldTool = {
216
+ name: 'delete_field',
217
+ label: 'Delete field',
218
+ description: 'Permanently delete a field (column) from an existing table.',
219
+ promptSnippet: 'delete_field: drop a column from a table by name. Destructive — gated.',
220
+ parameters: DeleteFieldSchema,
221
+ async execute(_id, params, _signal, _onUpdate, ctx) {
222
+ const resolved = await (0, context_1.resolveProjectContext)(ctx.cwd);
223
+ if (!resolved.context)
224
+ return fail(resolved.reason);
225
+ try {
226
+ const schema = await (0, schema_resolve_1.resolveSchema)(resolved.context);
227
+ const table = (0, schema_resolve_1.resolveTable)(schema, params.table_name);
228
+ const field = (0, schema_resolve_1.resolveField)(table, params.field_name);
229
+ await resolved.context.api.field
230
+ .delete({ where: { id: field.id }, select: { id: true } })
231
+ .unwrap();
232
+ return ok(`Deleted field ${params.table_name}.${params.field_name}`);
233
+ }
234
+ catch (err) {
235
+ return fail(err instanceof Error ? err.message : 'Failed to delete field');
236
+ }
237
+ },
238
+ };
@@ -0,0 +1,22 @@
1
+ import type { ToolDefinition } from '@earendil-works/pi-coding-agent';
2
+ declare const BlueprintSchema: import("typebox/type").TSchema;
3
+ type ProvisionedField = {
4
+ name: string;
5
+ type: string;
6
+ isRequired: boolean;
7
+ defaultValue: string | null;
8
+ };
9
+ type ProvisionedTable = {
10
+ name: string;
11
+ fields: ProvisionedField[];
12
+ policies: string[];
13
+ relationCount: number;
14
+ };
15
+ export type ProvisionBlueprintDetails = {
16
+ created: number;
17
+ total: number;
18
+ tables: ProvisionedTable[];
19
+ error: string | null;
20
+ };
21
+ export declare const provisionBlueprintTool: ToolDefinition<typeof BlueprintSchema, ProvisionBlueprintDetails>;
22
+ export {};